JUMP TO:
1. aspect-ratio
2. Logical Properties
3. Cascade Layers
4. content-visibility
1. aspect-ratio
aspect-ratio is a modern CSS property that lets you tell the browser “this box should keep this width-to-height proportion” — without hacks, wrappers, or JavaScript.
What aspect-ratio does
aspect-ratio: 16 / 9;
This means:
For every 16 units of width, keep 9 units of height.
The browser will calculate the missing dimension automatically.
The simplest example
<div class="box"></div>
.box {
width: 300px;
aspect-ratio: 16 / 9;
background: lightgray;
}
Result:
- width =
300px - height = automatically
168.75px
No padding-bottom tricks needed.
The key rule
aspect-ratioonly matters when ONE dimension is missing
If both width and height are set, aspect ratio is ignored.
/* aspect-ratio is ignored */
.box {
width: 300px;
height: 200px;
aspect-ratio: 16 / 9;
}
Common real-world use cases
1️⃣ Responsive images / videos
.video {
aspect-ratio: 16 / 9;
}
.video iframe {
width: 100%;
height: 100%;
}
Used for:
- YouTube / Vimeo
- embedded maps
- iframes
2️⃣ Image placeholders (no layout shift)
img {
aspect-ratio: 3 / 2;
width: 100%;
object-fit: cover;
}
Prevents content jumping while images load (important for performance).
3️⃣ Cards with consistent shapes
.card-image {
aspect-ratio: 1 / 1;
background-size: cover;
}
Perfect for:
- product grids
- galleries
- blog thumbnails
Aspect ratio with Grid & Flexbox
Grid example
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.item {
aspect-ratio: 4 / 3;
background: #ddd;
}
Each item:
- auto-sizes height
- stays proportional
- fills the grid cell width
Aspect ratio on replaced elements (images, video)
Images already have an intrinsic aspect ratio.
img {
width: 100%;
height: auto;
}
But CSS aspect-ratio can override or enforce consistency:
img {
aspect-ratio: 1 / 1;
object-fit: cover;
}
aspect-ratio vs the old padding hack
Old way (avoid now)
.box {
position: relative;
padding-bottom: 56.25%; /* 16:9 */
}
.box iframe {
position: absolute;
inset: 0;
}
New way (preferred)
.box {
aspect-ratio: 16 / 9;
}
Cleaner, readable, maintainable.
Browser support (important part)
Current support (modern reality)
- ✅ Chrome
- ✅ Edge
- ✅ Firefox
- ✅ Safari
- ✅ iOS Safari
- ✅ Android Chrome
Support is excellent in all modern browsers.
Older browsers
- ❌ Internet Explorer
- ⚠️ Very old mobile browsers
If you still support IE, you need the padding hack as a fallback.
Safe fallback pattern (progressive enhancement)
.box {
height: 0;
padding-bottom: 56.25%;
}
@supports (aspect-ratio: 1 / 1) {
.box {
height: auto;
padding-bottom: 0;
aspect-ratio: 16 / 9;
}
}
Interaction with width, max-width, min-width
.box {
width: 100%;
max-width: 400px;
aspect-ratio: 1 / 1;
}
The browser:
- Calculates width
- Derives height from aspect ratio
- Applies constraints
Mental model (easy to remember)
- Width known → height is calculated
- Height known → width is calculated
- Both known → aspect-ratio ignored
2. Logical Properties
CSS logical properties may feel “optional” at first, but once they click, they make a lot of sense—especially for internationalisation and modern layouts.
The core idea (plain English)
Physical properties describe screen directions:
- top / right / bottom / left
Logical properties describe content flow:
- start / end
- block / inline
So instead of saying:
“Put space on the top”
you say:
“Put space at the start of the block direction”
Block vs inline (most important concept)
In normal English text (default)
writing-mode: horizontal-tb;
direction: ltr;
- Block direction → top → bottom
- Inline direction → left → right
So:
| Logical | Physical equivalent |
|---|---|
block-start | top |
block-end | bottom |
inline-start | left |
inline-end | right |
margin-block-start
.card {
margin-block-start: 1rem;
}
This means:
“Add margin at the start of the block flow”
In normal English text, this behaves exactly like:
margin-top: 1rem;
Why not margin-start?
Because start of what?
CSS separates two axes:
- Block axis → paragraphs stack
- Inline axis → text flows
So you must specify which axis:
margin-block-startmargin-inline-start
There is no plain margin-start.
Common logical property mappings
Margins & padding
| Physical | Logical |
|---|---|
margin-top | margin-block-start |
margin-bottom | margin-block-end |
margin-left | margin-inline-start |
margin-right | margin-inline-end |
padding-left | padding-inline-start |
Size & positioning
| Physical | Logical |
|---|---|
width | inline-size |
height | block-size |
top | inset-block-start |
left | inset-inline-start |
Example:
.box {
inline-size: 300px;
block-size: 200px;
}
Why logical properties exist (the real reason)
They exist for internationalisation and writing modes.
Right-to-left languages (Arabic, Hebrew)
direction: rtl;
inline-startbecomes rightinline-endbecomes left
Your layout automatically flips — no extra CSS.
Vertical writing systems (Japanese, Chinese)
writing-mode: vertical-rl;
Now:
- block direction → right → left
- inline direction → top → bottom
Logical properties adapt.
Physical ones break.
Example: why logical beats physical
Bad (physical)
.card {
margin-left: 1rem;
}
RTL users get spacing on the wrong side.
Good (logical)
.card {
margin-inline-start: 1rem;
}
Correct for:
- LTR
- RTL
- vertical text
Shorthand logical properties
margin-block: 1rem 2rem;
margin-inline: 0.5rem 1rem;
Equivalent to:
margin-top / bottom
margin-left / right
…but flow-aware.
Browser support as of 2026
- ✅ Chrome
- ✅ Edge
- ✅ Firefox
- ✅ Safari
- ✅ Mobile browsers
Support is excellent for margins, padding, sizes, and insets.
Should you use them everywhere?
Use logical properties when:
- building reusable components
- supporting RTL languages
- writing design systems
- working with Grid / Flex layouts
Physical properties are still fine when:
- doing one-off visual tweaks
- working on legacy code
- intentionally breaking symmetry
3. Cascade Layers
Cascade layers are a modern CSS feature that give you explicit control over the cascade itself — not just selectors and specificity.
What cascade layers are (in one sentence)
Cascade layers (
@layer) let you define ordered “buckets” of CSS so some styles always win over others, regardless of selector specificity.
Why they exist (the problem they solve)
Traditionally, CSS priority is decided by:
- Origin (browser → user → author)
- Importance (
!important) - Specificity
- Source order
This breaks down when:
- mixing framework CSS, theme CSS, and custom overrides
- specificity wars (
.foo .bar div span) - using
!importanteverywhere
Layers let you say:
“These styles are more important than those styles — full stop.”
The simplest example
@layer base {
h1 {
color: black;
}
}
@layer overrides {
h1 {
color: blue;
}
}
Even though both selectors are identical, overrides wins because it’s defined later in the layer order.
Declaring layer order (best practice)
@layer reset, base, components, utilities;
This sets the priority once, at the top of your CSS.
reset→ lowest priorityutilities→ highest priority
Putting CSS into layers
@layer reset {
* {
margin: 0;
}
}
@layer base {
body {
font-family: system-ui, sans-serif;
}
}
@layer components {
.card {
padding: 1rem;
}
}
@layer utilities {
.text-center {
text-align: center;
}
}
Key rule to remember (very important)
Layer order beats specificity.
This wins:
@layer utilities {
h1 {
color: red;
}
}
Over this:
@layer base {
h1.special.title {
color: green;
}
}
Even though the second selector is much more specific.
Where unlayered CSS fits
Unlayered CSS sits above all layers.
h1 {
color: purple;
}
This will override any layered rule.
This is intentional and useful for quick overrides.
Nesting layers
@layer framework.components {
.btn {
padding: 1rem;
}
}
Creates:
frameworkframework.components
Order is still controlled at the top.
Layers vs !important
@layer | !important |
|---|---|
| Structural | Emergency override |
| Predictable | Hard to reason about |
| Scales well | Causes conflicts |
| Encouraged | Avoid when possible |
4. content-visibility
content-visibility is a performance-focused CSS property that tells the browser whether it should bother rendering an element’s contents right now.
It’s powerful, but subtle.
What content-visibility does (plain English)
content-visibility: auto;
Means:
“If this element is off-screen, skip layout, paint, and rendering of its contents until it’s needed.”
This can dramatically improve:
- initial page load
- time to first render
- scrolling performance
The three values you’ll actually use
visible (default)
content-visibility: visible;
Browser renders everything as normal.
hidden
content-visibility: hidden;
- Contents are not rendered
- Element still takes up space
- Similar to
visibility: hidden, but stronger
auto (the important one)
.content {
content-visibility: auto;
}
- Browser renders content only when near viewport
- Automatically “turns on” when scrolled into view
- No JavaScript required
Simple example
<section class="article">
<h2>Article title</h2>
<p>Lots of text…</p>
</section>
.article {
content-visibility: auto;
}
If the section is far below the fold:
- browser skips rendering it
- page loads faster
Very important companion: contain-intrinsic-size
When content isn’t rendered yet, the browser needs to know how much space to reserve.
.article {
content-visibility: auto;
contain-intrinsic-size: 1000px;
}
This:
- prevents layout jumps
- gives the browser a placeholder size
You can also do:
contain-intrinsic-size: 800px 300px;
(height width)
Where content-visibility shines
1️⃣ Long pages
- blog posts
- documentation
- product listings
- event archives (very relevant to WordPress sites)
.post {
content-visibility: auto;
contain-intrinsic-size: 600px;
}
2️⃣ Repeated heavy components
- image galleries
- maps
- embeds
- sliders
3️⃣ Tab panels / accordions
.tab-panel {
content-visibility: hidden;
}
Then switch to visible when active.
Where not to use it
🚫 Above-the-fold content
🚫 Small components
🚫 Elements that rely on size immediately (JS measurements)
🚫 Anything needing immediate accessibility focus
Interaction with JavaScript (important gotcha)
If JS queries layout info:
element.getBoundingClientRect();
And the element is not rendered yet:
- values may be zero or delayed
- measurements update only when visible
So:
Don’t use
content-visibilityon elements JS depends on immediately.
Accessibility notes
- Screen readers can still access content
- Focused elements force rendering
- Safe when used for large, scroll-based content
Still, test with:
- keyboard navigation
- screen readers

Leave a Reply