New and emerging CSS, crawled from specs, browser blogs, and expert writeups•Almost entirely built by Claude Code•Last updated Aug 21, 2026•New and emerging CSS, crawled from specs, browser blogs, and expert writeups•Almost entirely built by Claude Code•Last updated Aug 21, 2026•New and emerging CSS, crawled from specs, browser blogs, and expert writeups•Almost entirely built by Claude Code•Last updated Aug 21, 2026•New and emerging CSS, crawled from specs, browser blogs, and expert writeups•Almost entirely built by Claude Code•Last updated Aug 21, 2026•New and emerging CSS, crawled from specs, browser blogs, and expert writeups•Almost entirely built by Claude Code•Last updated Aug 21, 2026•New and emerging CSS, crawled from specs, browser blogs, and expert writeups•Almost entirely built by Claude Code•Last updated Aug 21, 2026•New and emerging CSS, crawled from specs, browser blogs, and expert writeups•Almost entirely built by Claude Code•Last updated Aug 21, 2026•New and emerging CSS, crawled from specs, browser blogs, and expert writeups•Almost entirely built by Claude Code•Last updated Aug 21, 2026•
Setting inert on an element — via the plain HTML attribute or the matching el.inert JS property — removes it and everything inside it from click handling, tab order, text selection, and the accessibility tree, all in one step. Opening the modal below marks the background section inert instead of juggling tabindex="-1" on every focusable descendant by hand; closing it restores normal interaction.
View code
HTML
<div class="demo-area">
<div id="background" class="panel">
<p style="margin:0">Background content</p>
<input type="text" placeholder="Try typing or tabbing here">
<button id="bg-btn">Click me</button>
</div>
<button id="open-btn" class="open-btn">Open modal</button>
<div id="modal" class="modal">
<p style="margin:0">I'm a modal. The background is now inert — try tabbing, clicking, or typing behind me.</p>
<button id="close-btn">Close</button>
</div>
</div>
Staggering a list reveal with an inline custom-property index
custom properties, calc(), animation-delay
Each item carries its own position as an inline custom property (style="--i: 2"), and a single shared animation-delay: calc(var(--i) * 90ms) rule turns that index into a staggered entrance — one keyframe animation and one CSS rule cover every item, with no per-item rules or JavaScript needed.
A back-to-top button that hides itself with @container scroll-state()
@container scroll-state(scrollable)
container-type: scroll-state on the root element exposes whether the page has more content to scroll toward a given edge, so @container scroll-state(scrollable: top) can slide a back-to-top link into view only once the user has actually scrolled down — no scroll event listener, no JavaScript.
View code
HTML
<div class="page-content">
<p>Scroll down to reveal the back-to-top button ↓</p>
<p>Paragraph two of filler content to make this page scrollable.</p>
<p>Paragraph three of filler content.</p>
<p>Paragraph four of filler content.</p>
<p>Paragraph five of filler content.</p>
<p>Paragraph six of filler content.</p>
<p>Paragraph seven of filler content.</p>
<p>Paragraph eight of filler content.</p>
<p>Paragraph nine of filler content.</p>
<p>Paragraph ten of filler content.</p>
<p>Paragraph eleven of filler content.</p>
<p>Paragraph twelve of filler content.</p>
<p>Paragraph thirteen of filler content.</p>
<p>Paragraph fourteen of filler content.</p>
<p>Paragraph fifteen — end of content.</p>
</div>
<a href="#top" class="back-to-top" aria-label="Back to top">↑</a>
A single repeating track, animated by one @keyframes rule that translates it by exactly -50% of its own width, creates an infinite horizontal marquee with no JavaScript. The content is duplicated enough times that the track is always wider than the container, so there is no visible gap when the loop restarts — and every duplicate past the first is aria-hidden, so screen readers hear it exactly once.
Real radio inputs and labels form an accessible rating control, styled entirely with :has() and general sibling selectors — :has(~ .star:hover) reaches backwards to light up every star before the hovered one, the same trick fills stars up to whichever value is :checked, and a CSS counter turns that fill state directly into a live rating number, all without JavaScript.
scroll-marker-group: after generates a ::scroll-marker-group container for a scroll-snap carousel, and giving each slide a ::scroll-marker turns it into a clickable dot placed in that group automatically — the browser tracks which one is current via :target-current, so active-dot highlighting needs no JavaScript at all.
Plain calc() can't accept keyword values like auto, fit-content, or max-content as an operand. calc-size(fit-content, size + 40px) can — its first argument is the keyword to resolve, and size stands in for that resolved value inside the expression, so an element can size itself relative to its own intrinsic content plus a fixed offset.
Animating an element along a curve with offset-path
offset-path, offset-distance, offset-rotate
offset-path, offset-distance, and offset-rotate move an element along an arbitrary SVG path — a Bézier curve, a loop, any custom shape — with offset-rotate: auto keeping it tangent to the curve as it travels, so a single @keyframes animation on offset-distance replaces manual left/top math or a JS animation library.
Detecting which card is snapped with @container scroll-state()
@container scroll-state(snapped)
container-type: scroll-state turns a scroll-snapping element into a query container, so a descendant can react via @container scroll-state(snapped: inline) to whether that specific card is the one currently snapped into view — the active state for a carousel or slider, computed entirely by the browser with no IntersectionObserver or scroll event listener.
Animating a rotating gradient border with @property
@property, conic-gradient()
Registering --angle with @property makes it an animatable <angle>, so a conic-gradient() painted onto the border-box (with a plain fill clipped to the padding-box) can spin smoothly via a keyframe animation — no rotating pseudo-element or JavaScript required.
animation-timeline: view() ties a keyframe animation to how far an element has scrolled through its nearest scrollable ancestor, so each box can fade and slide in exactly as it enters the viewport — a common scroll-reveal effect that used to need an IntersectionObserver, now built entirely in CSS.
A dropdown menu with anchor positioning and automatic flipping
anchor-name, position-try-fallbacks
Pairing the popover attribute with anchor-name, position-anchor, and position-try-fallbacks builds a dropdown menu that opens directly below its trigger button and automatically flips above it when there's no room below — all without a single line of positioning JavaScript.
interpolate-size: allow-keywords lets a transition genuinely animate toward auto, so an expanding panel no longer needs a guessed max-height or a JavaScript-measured pixel value to animate smoothly to its real content height.
View code
HTML
<div class="expander">
<input type="checkbox" id="exp1" class="toggle">
<label for="exp1">Toggle details</label>
<div class="content">
<p>This panel animates to its natural height using interpolate-size, without a guessed max-height or JavaScript measuring the content.</p>
</div>
</div>
Drawing lines in grid gaps with row-rule and column-rule
row-rule, column-rule
The CSS Gap Decorations module extends column-rule to grid and flexbox and adds a matching row-rule, so dividers between grid cells can be styled directly, without extra spacer elements or background-image tricks.
Switching styles by a container's custom property with @container style()
@container style()
@container style() queries a container's own computed custom-property value, not just its size, so a component can react to a --theme flag set anywhere in its ancestor chain without that flag being repeated as a class name or a media query.
Automatic accessible text color with contrast-color()
contrast-color()
contrast-color() picks black or white for you, whichever contrasts better against a given color, so a badge painted from a single dynamic background variable keeps readable text automatically instead of a text color hand-picked for every theme.
Scattering confetti with the CSS random() function
random()
random() generates a random number natively in CSS, so positions, rotations, and hues for things like confetti or particle effects can vary on every page load straight from a stylesheet, without JavaScript or a hand-rolled custom property per element. Browsers without support fall back to a static pattern via @supports.
Staggered bar chart from sibling-index() and sibling-count()
sibling-index(), sibling-count()
sibling-index() and sibling-count() expose an element's position and total sibling count directly as CSS numbers, so per-item height, hue, and animation delay can be derived with plain calc() math instead of inline custom properties, nth-child selectors, or a scripting loop.
Positioning elements around a circle with sin() and cos()
sin(), cos()
CSS trigonometric functions (sin(), cos(), and friends) compute coordinates directly in CSS, so dots on a dial or items in a radial menu can be placed around a circle from one angle custom property instead of hand-typed positions or a JavaScript loop.
Creates organic blob shapes from a single div using the multi-value border-radius shorthand — the slash separates horizontal radii from vertical radii, and animating between value sets makes the blob feel alive.
::checkmark exposes the checkmark/tick glyph inside a native checkbox or radio button as a styleable pseudo-element, so its shape and color can be customized without discarding native accessibility.
round() snaps a value to the nearest multiple of a step — with nearest, up, and down strategies — useful for aligning computed sizes to a pixel or spacing grid without a build-time calculation.
Writing range media queries with comparison operators
@media (width >= 600px) and (width <= 1200px)
@media (400px <= width <= 700px) reads like an actual inequality instead of two separate min-width/max-width declarations — a small but genuinely more readable syntax for range-based breakpoints.
View code
HTML
<div class="box">Resize the preview panel — I only turn blue between 400px and 700px wide.</div>
Deriving transparent variants with relative color syntax
oklch(from color l c h / α)
oklch(from color l c h / alpha) reads a color's own channels back out and lets you recompute just one of them — here, deriving four opacity levels of the same hue without four separate rgba() variables.
View code
HTML
<div class="row">
<div class="chip" style="background: var(--base)"></div>
<div class="chip" style="background: oklch(from var(--base) l c h / 0.7)"></div>
<div class="chip" style="background: oklch(from var(--base) l c h / 0.4)"></div>
<div class="chip" style="background: oklch(from var(--base) l c h / 0.15)"></div>
</div>
flex-wrap: balance spreads items across wrapped rows more evenly than the default greedy wrap, so the last row doesn't end up with a single lonely leftover item.
@supports at-rule(@layer) tests whether the browser understands a given at-rule before you rely on it, extending progressive enhancement to newer at-rules like @layer, @property, and @scope.
light-dark() now accepts image values — gradients, url() references — not just colors, so a single declaration can swap an entire background image between a light and dark variant automatically.
View code
HTML
<button id="theme-toggle" type="button">Toggle theme</button>
<div class="swatch" id="swatch"></div>
<p class="note">If the box above doesn't visibly change, your browser doesn't yet support <code>light-dark()</code> with image values — this is one of the newest, least-supported capabilities in the gallery.</p>
Lets a sticky element track a different scroll container per axis, so more flexible split-pane and dashboard layouts can each keep their own header pinned independently.
View code
HTML
<div class="grid-2">
<div class="col">
<div class="sticky-item">Sticky in this column</div>
<div class="filler"></div>
</div>
<div class="col">
<div class="filler tall"></div>
</div>
</div>
A fixed bar's scaleX is driven directly by a named scroll-timeline, so it grows in perfect sync with how far the user has scrolled — a page-level reading indicator with no scroll event listener at all.
View code
HTML
<div class="page">
<div class="bar"><div class="fill"></div></div>
<div class="content">
<p>Scroll down to fill the bar ↓</p>
<p>The bar grows in sync with your scroll position, using a named scroll timeline — no JavaScript.</p>
<p>The timeline is declared on this scrollable area and shared with the bar outside it via <code>timeline-scope</code>.</p>
<p>Scroll back up to watch it shrink.</p>
<p>Keep scrolling…</p>
<p>Almost there…</p>
<p>End of content.</p>
</div>
</div>
CSS
body { font-family: system-ui, sans-serif; margin: 2rem; background: #fff; color: #111827; }
.page {
border: 1px solid #e5e7eb;
border-radius: 8px;
overflow: hidden;
/* Makes the named timeline declared on .content visible to .fill, a sibling of .content */
timeline-scope: --page-scroll;
}
.bar { height: 5px; background: #e5e7eb; }
.fill {
height: 100%;
background: #2563eb;
transform-origin: left;
animation: fill-bar linear both;
animation-timeline: --page-scroll;
}
@keyframes fill-bar { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.content {
height: 200px;
overflow-y: auto;
padding: 1rem;
scroll-timeline-name: --page-scroll;
scroll-timeline-axis: block;
}
.content p { font-size: 0.9rem; color: #4b5563; margin: 0 0 2rem 0; }
code { background: #f3f4f6; padding: 0 0.25rem; border-radius: 4px; }
The shape() function draws an arbitrary border or background outline from move/line/curve commands — blob shapes, notches, and cut-outs that used to require an SVG or clip-path hack.
View code
HTML
<div class="shapes">
<div class="shape blob"></div>
<div class="shape outline"></div>
<div class="shape cutout"></div>
</div>
<p class="note">Same organic path, three ways: a filled blob, a stroked outline, and a square-outside/organic-inside cut-out. Requires browser support for <code>border-shape</code> — shows as plain boxes otherwise.</p>
CSS
body { font-family: system-ui, sans-serif; margin: 2rem; background: #fff; color: #1f1f1f; display: flex; flex-direction: column; align-items: center; gap: 1rem; }
.shapes { display: flex; gap: 1rem; align-items: center; }
.shape { width: 90px; height: 90px; }
/* 1. Solid blob — background follows the shape */
.blob {
background: rebeccapurple;
border-shape: shape(from 50% 0%, curve to 100% 40% with 100% 0%, curve to 60% 100% with 100% 80%, curve to 0% 60% with 30% 100%, curve to 50% 0% with 0% 30%, close);
}
/* 2. Organic outline — border hugs the shape */
.outline {
border: 8px solid rebeccapurple;
border-shape: shape(from 50% 0%, curve to 100% 40% with 100% 0%, curve to 60% 100% with 100% 80%, curve to 0% 60% with 30% 100%, curve to 50% 0% with 0% 30%, close);
}
/* 3. Inner cut-out — square outside, organic inside */
.cutout {
border: 12px solid rebeccapurple;
border-shape: inset(0) shape(from 50% 0%, curve to 100% 40% with 100% 0%, curve to 60% 100% with 100% 80%, curve to 0% 60% with 30% 100%, curve to 50% 0% with 0% 30%, close);
}
.note { max-width: 320px; text-align: center; font-size: 0.8rem; color: #6b6b6b; }
code { background: #eeeeee; padding: 0 0.25rem; border-radius: 4px; }
A masonry-style grid track lets items of different heights pack tightly into the next available slot, matching the classic Pinterest-style layout without a JavaScript layout library.
View code
HTML
<div class="masonry">
<div class="tile" style="height:60px">1</div>
<div class="tile" style="height:100px">2</div>
<div class="tile" style="height:40px">3</div>
<div class="tile" style="height:80px">4</div>
<div class="tile" style="height:50px">5</div>
<div class="tile" style="height:70px">6</div>
</div>
<p class="note">Uses <code>column-count</code> as a universal fallback so it always looks right, then upgrades to real masonry where <code>grid-template-rows: masonry</code> or <code>display: grid-lanes</code> is supported.</p>
position: sticky with staggered offsets piles cards on top of each other as the user scrolls, while a view() animation-timeline scales each card down as the next one slides over it.
Each section declares a named view-timeline; the matching table-of-contents link consumes that timeline to animate its own active state as the section scrolls through the viewport — no IntersectionObserver required.
View code
HTML
<div class="layout">
<nav class="toc">
<a href="#s1" class="toc-link">① Intro</a>
<a href="#s2" class="toc-link">② Methods</a>
<a href="#s3" class="toc-link">③ Results</a>
</nav>
<div class="sections">
<section id="s1" class="section">
<h3>Introduction</h3>
<p>Scroll down — the active link updates with no JavaScript at all.</p>
</section>
<section id="s2" class="section">
<h3>Methods</h3>
<p>Each section has a named <code>view-timeline-name</code>. The wrapper shares it with <code>timeline-scope</code>.</p>
</section>
<section id="s3" class="section">
<h3>Results</h3>
<p>The matching nav link consumes the timeline via <code>animation-timeline</code>.</p>
</section>
</div>
</div>
A scroll-driven animation tied to a named scroll-timeline shrinks a position: sticky header's padding over the first stretch of scroll — a common product-site effect that used to need a scroll event listener.
View code
HTML
<div class="scroll-area">
<header class="site-header">
<span class="logo">BRAND</span>
<span class="hint">Scroll down ↓</span>
</header>
<div class="content">
<p>The header above is <code>position: sticky</code>.</p>
<p>Its padding shrinks as you scroll, driven by <code>scroll(nearest block)</code> — no JS.</p>
<p>Scroll back up to watch it expand again.</p>
<p>Works on any scrollable container, not just the page root.</p>
<p>Keep scrolling to see the full effect play out smoothly.</p>
</div>
</div>
::search-text and ::search-text:current let a page style the highlight the browser draws around a user's Ctrl/Cmd+F search matches, distinguishing the currently-focused match from the rest.
View code
HTML
<p>The <span class="match">accent-color</span> property. Current match: <span class="match current">accent</span> styled with <code>:current</code>.</p>
<p class="note">These highlights are simulated here — <code>::search-text</code> only applies to real matches from the browser's Ctrl/Cmd+F Find in Page.</p>
Tailwind: grid-rows-[0fr] + peer-open:grid-rows-[1fr] on the sibling element
Native <details>/<summary> plus a transition from grid-template-rows: 0fr to 1fr collapses and reveals content smoothly — CSS can interpolate fr track sizes even though it still can't transition height: auto. Giving each <details> the same name attribute also turns them into an exclusive group, so opening one closes the others with zero JavaScript.
View code
HTML
<div class="accordion">
<div class="accordion-item">
<details name="accordion-demo">
<summary aria-controls="accordion-content-1">
What is the 0fr → 1fr trick?
<svg class="chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</summary>
</details>
<div class="content" id="accordion-content-1">
<div class="body">
Wrapping the content in a grid row and transitioning <code>grid-template-rows</code> from <code>0fr</code> to <code>1fr</code> collapses and reveals it smoothly — CSS can interpolate <code>fr</code> track sizes even though it still can't transition <code>height: auto</code>.
</div>
</div>
</div>
<div class="accordion-item">
<details name="accordion-demo">
<summary aria-controls="accordion-content-2">
Why put the content outside <details>?
<svg class="chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</summary>
</details>
<div class="content" id="accordion-content-2">
<div class="body">
Placing the animated div as a sibling of <code><details></code> sidesteps Chrome's <code>::details-content</code> / <code>content-visibility</code> mechanism, which would otherwise block a custom height transition from running.
</div>
</div>
</div>
<div class="accordion-item">
<details name="accordion-demo">
<summary aria-controls="accordion-content-3">
How do exclusive groups work?
<svg class="chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</summary>
</details>
<div class="content" id="accordion-content-3">
<div class="body">
Give each <code><details></code> element the same <code>name</code> attribute and the browser enforces that only one can be open at a time — try opening this one, the others will close automatically, no JavaScript required.
</div>
</div>
</div>
</div>
mod() and rem() join calc() as real CSS math functions, so patterns like alternating row shades or repeating layouts can be computed from an index directly in CSS instead of a build-time loop.
Tailwind: v3.2+ accent-* utilities, e.g. accent-blue-500
accent-color restyles the checked state of checkboxes, radio buttons, range sliders, and progress bars to match a brand color, without abandoning the browser's native, accessible control rendering.
@scope (.root) limits a block of rules to matches inside that root — and can even carve out an inner boundary — giving plain CSS a form of the encapsulation Shadow DOM or naming conventions used to require.
View code
HTML
<div class="scope-a">
<p>Blue text here (inside .scope-a)</p>
</div>
<div class="scope-b">
<p>Pink text here (inside .scope-b)</p>
</div>
Tailwind: v3.4+ user-valid: and user-invalid: variants
:user-valid and :user-invalid match like :valid/:invalid, but only once the user has actually interacted with the field — so a required input doesn't show an alarming red error before anyone's had a chance to type.
popover and popovertarget give any element top-layer rendering, light-dismiss on outside click, Escape-to-close, and automatic focus handling — all the fiddly behavior a hand-rolled dropdown used to need JS for.
View code
HTML
<button popovertarget="pop">Toggle popover</button>
<div id="pop" popover>
<p>I'm a native popover — top-layer, dismisses on outside click or Esc, no JS.</p>
</div>
The commandfor and command attributes let a plain <button> show, close, or toggle a <dialog> or popover directly from HTML — no click listener or imperative .showModal() call needed.
View code
HTML
<button commandfor="dlg" command="show-modal">Show dialog (no JS!)</button>
<dialog id="dlg" aria-label="Example dialog">
<p>Opened declaratively via <code>commandfor</code> + <code>command</code>.</p>
<button commandfor="dlg" command="close">Close</button>
</dialog>
Fully restyling <select> with appearance: base-select
appearance: base-select
Opts a native <select> into a styleable rendering mode, including its dropdown picker and options, so teams can finally style a real <select> instead of reaching for a custom-built listbox component.
Tailwind: v4's entire default color palette is defined in oklch for consistent perceived brightness
oklch(lightness chroma hue) keeps perceived brightness consistent as hue changes, unlike hsl() — which makes it much easier to build accessible color scales that don't have surprise-dark or surprise-bright entries.
View code
HTML
<div class="row">
<div class="swatch" style="background:oklch(70% 0.2 20)"></div>
<div class="swatch" style="background:oklch(70% 0.2 90)"></div>
<div class="swatch" style="background:oklch(70% 0.2 160)"></div>
<div class="swatch" style="background:oklch(70% 0.2 230)"></div>
<div class="swatch" style="background:oklch(70% 0.2 300)"></div>
</div>
<p class="note">Same lightness (70%) and chroma (0.2) at five different hues — they all look equally bright.</p>
color-mix() blends two colors by a percentage in a chosen color space (like oklch or srgb), replacing the Sass/JS color-math functions teams used to reach for to generate tints and shades.
Trimming invisible leading space above and below text
text-box: cap alphabetic
text-box (a shorthand for text-box-trim and text-box-edge) removes the half-leading space baked into every line box, so icons, badges, and single-line labels can finally align on their actual glyph edges.
View code
HTML
<div class="row">
<span class="pill default">SEND</span>
<span class="pill trimmed">SEND</span>
</div>
<p class="note">Same padding on both — the second uses <code>text-box: cap alphabetic</code> to remove the invisible space above/below the capital letters.</p>
Sets a target x-height ratio so a fallback font renders at a visually matched size to the intended web font, reducing the layout jump that happens while a custom font is still loading.
View code
HTML
<p class="no-adjust">Fallback font (no font-size-adjust)</p>
<p class="adjusted">Fallback font (font-size-adjust: 0.5)</p>
svh, lvh and dvh give three explicit answers to 'how tall is the viewport': smallest, largest, and dynamically-updating-with-the-browser-UI — ending the classic issue where 100vh overshoots the visible area on mobile.
View code
HTML
<div class="row">
<div class="bar vh">100vh</div>
<div class="bar svh">100svh</div>
<div class="bar dvh">100dvh</div>
</div>
<p class="note">On a real mobile browser, the address bar showing/hiding changes <code>dvh</code> live while <code>vh</code> stays locked to the largest size.</p>
Tailwind: v3.4+ text-balance and text-pretty utilities
text-wrap: balance evens out the number of characters per line so multi-line headings don't end in a single lonely word — a one-line fix for a problem designers used to solve by hand with manual line breaks.
View code
HTML
<h2 class="balanced">Design systems make teams faster and products more consistent</h2>
<hr>
<h2 class="unbalanced">Design systems make teams faster and products more consistent</h2>
Tailwind: v4 starting: variant, e.g. starting:opacity-0
Defines the 'before' state for an element that's just been inserted into the DOM (or switched from display:none), so entry transitions finally work with plain CSS transitions instead of a JS-timed class toggle.
View code
HTML
<button id="add-btn">Add a card</button>
<div id="list" class="list"></div>
anchor-name and position-anchor tether a positioned element to any other element on the page via anchor() functions — the CSS replacement for JS-based tooltip and popover positioning libraries.
Morphing between UI states with the View Transitions API
view-transition-name
document.startViewTransition() plus a shared view-transition-name lets an element smoothly morph into its new position or appearance across a DOM update — the kind of transition that used to require a JS animation library.
A named scroll-timeline turns any scrollable element into an animation driver, so progress bars and reveal effects can track scroll position with zero JavaScript and zero scroll event listeners.
Writing-direction-agnostic spacing with logical properties
padding-inline, margin-block…
Tailwind: Used natively throughout — px/py map to padding-inline/padding-block, ps/pe to the logical start/end
padding-inline, margin-block, border-inline-start and friends follow the document's writing direction instead of physical left/right/top/bottom, so the same rules work correctly in RTL and vertical writing modes for free.
Groups styles into named layers compared by declaration order first — a later layer always beats an earlier one regardless of selector specificity, making it far more predictable to override utility or third-party CSS.
View code
HTML
<button class="btn">Layered button</button>
CSS
@layer base, theme;
@layer base {
.btn { background: #6b7280; color: #fff; border: none; padding: 0.75rem 1.5rem; border-radius: 8px; font: inherit; font-weight: 600; cursor: pointer; }
}
@layer theme {
/* Wins over base despite equal specificity, because "theme" is declared after "base" */
.btn { background: #2563eb; }
}
body { font-family: system-ui, sans-serif; margin: 2rem; background: #fff; display: flex; justify-content: center; align-items: center; height: 160px; }
Tailwind: v3.2+ container utility, plus @container responsive variants like @lg:flex
container-type turns an element into a query container, so descendants can respond with @container rules based on the container's own width — true component-driven responsiveness instead of viewport-only media queries.
View code
HTML
<div class="resizable">
<div class="card">
<div class="thumb"></div>
<div class="body">
<h3>Card title</h3>
<p>Layout switches to a row once this container is wide enough.</p>
</div>
</div>
</div>
Tailwind: v3.4+ col-subgrid and row-subgrid utilities
Lets a nested grid inherit its parent's row or column tracks, so cards with different amounts of content can still line up their headings, body text, and buttons on the same rows — no extra wrapper markup or JS measuring needed.
View code
HTML
<div class="cards">
<article class="card">
<h3>Starter</h3>
<p>Good for trying things out.</p>
<button>Choose plan</button>
</article>
<article class="card">
<h3>Pro</h3>
<p>More storage, more seats, and priority support for growing teams that need it.</p>
<button>Choose plan</button>
</article>
<article class="card">
<h3>Enterprise</h3>
<p>Custom limits and a dedicated account manager.</p>
<button>Choose plan</button>
</article>
</div>
A length unit equal to the element's own computed line-height, so margins scale automatically when font-size changes instead of needing separate spacing values per heading level.
View code
HTML
<div class="rhythm">
<h2>Small heading</h2>
<p>This paragraph sits exactly one line-height below the heading above it, no matter that heading's font size.</p>
<h2 class="big">Big heading</h2>
<p>Same spacing rule, same visual rhythm — because the gap is defined in <code>lh</code>, not a fixed pixel value.</p>
</div>
Tailwind: v3.4+ has-* variant, e.g. has-[input:checked]:bg-blue-500
A relational pseudo-class that matches an element based on what's inside or after it, enabling long-requested 'parent selector' and 'previous sibling' style rules in plain CSS.
Registers a custom property with a type, inheritance behavior, and initial value — which, among other benefits, finally makes CSS custom properties animate smoothly like built-in properties instead of jumping instantly.
View code
HTML
<div class="swatch"></div>
<p>Hover the box — the color transition is smooth because <code>--highlight</code> is a registered, typed custom property.</p>
Units like cqi and cqw are sized relative to a query container's own dimensions rather than the viewport, so a component's type can scale fluidly based on the space it's actually given — a sidebar vs. a full-width card.
View code
HTML
<div class="resizable">
<div class="card">
<h3>Resize me</h3>
<p>Drag the bottom-right corner of the outer box. The heading font-size below is set in <code>cqi</code>, so it scales with this container's width — not the viewport.</p>
</div>
</div>
Lets form controls like <input> and <textarea> grow or shrink to fit their content with a single CSS property, replacing a common JavaScript auto-resize hack.
View code
HTML
<form>
<label>Default sizing
<input type="text" value="Type more, width stays fixed">
</label>
<label>field-sizing: content
<input class="auto-size" type="text" value="This one grows as you type">
</label>
</form>