Kaiber
Generate, edit, and beat-sync AI video with leading models in one workspace.
intrinsic-design
CRITICAL CSS SKILL: Use this whenever you are writing or reviewing CSS layouts, making components responsive, or the user mentions wanting fluid typography, responsive grids without breakpoints, avoiding media queries, container queries (@container), clamp(), minmax(), intrinsic design, or conten...
Full skill instructions
The concept of Intrinsic Web Design was introduced by Jen Simmons as a fundamental shift in how we think about responsive layout. Rather than imposing structure from outside (viewport breakpoints), intrinsic design lets the content itself dictate the layout — its natural size, aspect ratio, quantity, and context become the primary inputs for layout decisions.
The absence of media query breakpoints is a consequence of this approach, not its goal. When you design from the content outward, breakpoints become unnecessary for most layout work because the layout is already responding to the right signals.
This skill encodes that philosophy into concrete CSS patterns, drawing on Amit Sheen's technical implementation for the Frontend Masters blog.
Traditional responsive design asks: "How wide is the viewport?" and then dictates what the layout should look like at that width. Intrinsic design asks a fundamentally different question: "What does this content need?"
This shift has three dimensions:
Start every layout decision from the content. A card grid doesn't "become 3 columns at 1024px" — instead, its cards have a minimum readable width (say, 300px), and the browser fits as many as the available space allows. The column count is a result, never an input.
The natural characteristics of your content — text length, image aspect ratio, number of items, minimum readable size — are the real inputs to your layout system. When you encode these as constraints (minmax(), flex-basis, clamp()), the browser can make continuously correct layout decisions without you specifying every breakpoint.
If you find yourself writing 3-4 @media blocks to manage a single component, that's a signal you're imposing structure from outside. The fix isn't better breakpoints — it's rethinking the layout so the component can find its own shape.
This skill implements a four-method approach to make this philosophy concrete:
| Method | Purpose | Enables |
|---|---|---|
| Intrinsic Layouts | Structure adapts to content | Content finds its own column count |
| Fluid Values | Sizing scales continuously | Values respond proportionally to context |
| Container Units | Sizing relative to component | Components own their proportions |
| Container Queries | Structural shifts at component level | Components adapt to their actual space |
@media queries are reserved exclusively for:
hover, pointer, display-mode, update, scripting)prefers-color-scheme, prefers-reduced-motion, prefers-contrast, prefers-reduced-data)@media (min-width) or @media (max-width)Before writing any CSS, walk through these three steps:
What are the natural dimensions of this content? What's the minimum width where text remains readable? What aspect ratios do images have? How many items are typical? These observations become your layout constraints.
Instead of "3 columns on desktop, 1 on mobile," say: "Cards with a minimum width of 300px that fill the available space." Instead of "font-size: 24px on desktop, 16px on mobile," say: "Font size between 16px and 24px, scaling fluidly with the context."
Don't just resize your browser window. Place the same component in a sidebar (250px), a modal (500px), a main content area (800px), and a full-width hero. If it works in all of them without changes, your design is truly intrinsic.
Push adaptation into layout primitives. Instead of "at width X, force N columns," define constraints and let the browser derive the layout continuously.
❌ AVOID: Breakpoint-based grid
.card-grid {
display: grid;
gap: 1em;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.card-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
.card-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1280px) {
.card-grid { grid-template-columns: repeat(4, 1fr); }
}
✅ USE: Intrinsic grid
.card-grid {
display: grid;
gap: 1em;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}
This says: "Make as many columns as fit, but never let them get smaller than 320px." The browser handles the rest. One line replaces four breakpoint blocks.
❌ AVOID: Breakpoint sidebar toggle
.layout { display: block; }
.sidebar { width: 100%; }
.main { width: 100%; }
@media (min-width: 768px) {
.layout { display: flex; }
.sidebar { width: 240px; }
.main { flex: 1; }
}
✅ USE: Intrinsic flex layout
.layout {
display: flex;
flex-wrap: wrap;
}
.main {
/* main gets priority + readable minimum */
flex: calc(infinity) 1 360px;
}
.sidebar {
/* set sidebar idle size */
flex: 1 1 240px;
}
The calc(infinity) flex-grow ensures main always takes all available space. When the container is too narrow for both, flex-wrap: wrap causes the sidebar to drop below — no breakpoint needed.
| Pattern | CSS | Intent |
|---|---|---|
| Fluid grid | repeat(auto-fit, minmax(MIN, 1fr)) | Fill columns, respect minimum |
| Fluid grid (no stretch) | repeat(auto-fill, minmax(MIN, 1fr)) | Fill columns, don't stretch last row |
| Priority layout | flex: calc(infinity) 1 BASIS | Element gets all available space |
| Wrap sidebar | flex: 1 1 BASIS + parent flex-wrap: wrap | Stack when narrow |
Many "responsive" rules are just scalar tuning. Typography, spacing, radius, and component size should be continuous, not stepped.
❌ AVOID: Stepped values
.card {
font-size: 20px;
padding: 2em;
}
@media (max-width: 720px) {
.card { font-size: 18px; padding: 1.5em; }
}
@media (max-width: 380px) {
.card { font-size: 16px; padding: 1em; }
}
✅ USE: Fluid values with clamp()
.card {
font-size: clamp(16px, calc(11.529px + 1.176vi), 20px);
padding: clamp(1em, calc(-0.118em + 4.706vi), 2em);
}
The clamp() pattern: clamp(MIN, PREFERRED, MAX)
vi, vw)Use the vi unit (viewport inline) instead of vw for writing-mode-independent sizing.
:root {
/* Typography */
--text-sm: clamp(0.875rem, 0.8rem + 0.25vi, 1rem);
--text-base: clamp(1rem, 0.9rem + 0.5vi, 1.25rem);
--text-lg: clamp(1.25rem, 1rem + 1vi, 1.75rem);
--text-xl: clamp(1.5rem, 1rem + 1.5vi, 2.25rem);
--text-2xl: clamp(2rem, 1.2rem + 2.5vi, 3rem);
/* Spacing */
--space-xs: clamp(0.25rem, 0.15rem + 0.5vi, 0.5rem);
--space-sm: clamp(0.5rem, 0.3rem + 1vi, 1rem);
--space-md: clamp(1rem, 0.6rem + 2vi, 2rem);
--space-lg: clamp(1.5rem, 0.8rem + 3vi, 3rem);
--space-xl: clamp(2rem, 1rem + 4vi, 4rem);
/* Border radius */
--radius-sm: clamp(2px, 0.25vi, 6px);
--radius-md: clamp(4px, 0.5vi, 12px);
--radius-lg: clamp(8px, 1vi, 24px);
}
Use a clamp calculator to generate exact values from two anchor points:
The formula for the preferred value:
preferred = MIN + (MAX - MIN) × ((100vi - A) / (B - A))
Simplified to: calc(INTERCEPT + SLOPEvi)
Tools: clamp-calculator.netlify.app, utopia.fyi
When a component can render in a sidebar, modal, feed, or full-width layout, viewport-relative values produce unpredictable results. Container units solve this.
.card-container {
container-type: inline-size;
}
.card {
/* Font size scales with the card's actual width, not the viewport */
font-size: 5cqi;
/* Once font-size is set, em units cascade from it */
gap: 1em;
padding: 1.5em;
/* Or use clamp with container units */
border-radius: clamp(4px, calc(-16.87px + 6.522cqi), 64px);
}
| Unit | Meaning |
|---|---|
cqi | 1% of container's inline size |
cqb | 1% of container's block size |
cqw | 1% of container's width |
cqh | 1% of container's height |
cqmin | Smaller of cqi and cqb |
cqmax | Larger of cqi and cqb |
.card {
display: flex;
flex-wrap: wrap;
}
.card-content {
/* Content takes all available space but won't shrink below 40cqi */
flex: calc(infinity) 1 40cqi;
}
Same CSS, completely different results depending on the container's width. Three cards in different contexts adapt perfectly without any conditional logic.
When a component needs a true structural shift (not just scaling), use container queries. The key difference: we ask "How much space does this component have?" instead of "How wide is the screen?"
.container {
container-type: inline-size;
}
.group {
display: flex;
flex-direction: column;
gap: 1em;
@container (max-width: 32em) {
flex-direction: row;
gap: 0.5em;
.icon { font-size: 1.5em; }
p { display: none; }
}
}
| Situation | Use |
|---|---|
| Number of columns should adapt | auto-fit / minmax() (intrinsic) |
| Typography/spacing should scale | clamp() / container units |
| Layout direction should flip | Container query |
| Elements should show/hide | Container query |
| Completely different composition | Container query |
Media queries remain critical — but with a sharper mission. Use them exclusively for understanding the device and the user environment, not measuring pixels for layout.
/* Hover support — only apply hover effects on devices that support it */
@media (hover: hover) {
.link:hover { color: var(--color-accent); }
}
/* Pointer accuracy — enlarge touch targets on coarse pointers */
button {
padding: 1em;
@media (pointer: coarse) {
padding: 2em;
}
}
/* Display mode — adjust for picture-in-picture */
@media (display-mode: picture-in-picture) {
body { border: 2px solid white; }
}
/* Update frequency — reduce expensive effects on slow displays */
.glass-panel {
backdrop-filter: blur(16px);
@media (update: slow) {
backdrop-filter: none;
background: rgb(255 255 255 / 0.92);
}
}
/* Scripting availability */
.no-script-msg {
display: none;
@media (scripting: none) {
display: block;
}
}
The word "preferences" can be misleading. In many cases, these settings reflect real accessibility needs, not cosmetic choices.
/* Color scheme */
:root {
color-scheme: light;
@media (prefers-color-scheme: dark) {
color-scheme: dark;
}
}
/* Higher contrast */
.card {
color: #333;
background: #ccc;
@media (prefers-contrast: more) {
color: #000;
background: #fff;
}
}
/* Reduced data */
@media (prefers-reduced-data: reduce) {
.hero-video { display: none; }
}
/* Reduced motion — only animate when user has no preference */
.element {
@media (prefers-reduced-motion: no-preference) {
animation: spin 4s linear infinite;
}
}
When refactoring existing CSS to intrinsic design:
clamp(), min(), max() tokensauto-fit and minmax() over fixed column counts tied to viewport widthscontainer-type and introduce container units where helpfulhover, pointer, prefers-reduced-motion, and updateWhen writing new CSS, start by observing the content's intrinsic properties (natural size, aspect ratio, minimum readable width, item count). Then follow this decision path:
Is this a layout/grid decision?
├─ YES → Can auto-fit/minmax or flex-wrap handle it?
│ ├─ YES → Use intrinsic layout (Method 1)
│ └─ NO → Does the component need a structural shift?
│ ├─ YES → Use container query (Method 4)
│ └─ NO → Use intrinsic layout with constraints
│
Is this a scalar value (size, spacing, typography, radius)?
├─ YES → Should it scale with the viewport?
│ ├─ YES → Use clamp() with vi units (Method 2)
│ └─ NO → Should it scale with its container?
│ ├─ YES → Use container units cqi (Method 3)
│ └─ NO → Use fixed value or em/rem
│
Is this about device capability or user preference?
├─ YES → Use @media query (hover, pointer, prefers-*, etc.)
└─ NO → You probably don't need a conditional rule at all
When reviewing code, flag these patterns for refactoring:
| Anti-Pattern | Better Approach |
|---|---|
@media (min-width: 768px) { grid-template-columns: repeat(2, 1fr) } | repeat(auto-fit, minmax(320px, 1fr)) |
Multiple @media blocks changing only font-size | Single clamp() declaration |
Multiple @media blocks changing only padding/gap | Single clamp() declaration |
@media toggling flex-direction | flex-wrap: wrap + flex basis constraints |
Viewport units (vw, vh) inside reusable components | Container units (cqi, cqb) |
@media for hover effects | @media (hover: hover) |
| Animations without motion preference check | @media (prefers-reduced-motion: no-preference) |
The modern CSS techniques advocated in this skill (including Container Queries, clamp(), minmax(), viewport inline units vi, and interaction media queries) have been validated against current caniuse data and are safe for modern production web development.
All core features have reached Baseline: Widely Available status across all major browsers (Chrome, Safari, Firefox, Edge, iOS, Android), with the newest features like Container Size Queries and units (cqi) being fully supported since late 2022/early 2023. You do not need to provide polyfills or fallback viewport-width @media queries when using these intrinsic layout patterns.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience