Wait, I Only See h-48 for HeightβWhere's the Width? Understanding How CSS Grid Automatically Sizes Cards
I built my responsive grid with grid-cols-1 sm:grid-cols-2 md:grid-cols-3, and I see h-48 controlling the image height, but where's the width? π€ There's no w-48 or w-64 on my cards! How do they know how wide to be? My mentor dropped a knowledge bomb: "The grid controls width automaticallyβyou don't set it!" This blew my mind. The grid is the boss, and cards just obey. Understanding this changed how I think about responsive layouts! πβ¨
The Missing Width Mysteryβ
Me (Backend Dev): I just built this product grid:
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
<div className="bg-white rounded-lg shadow-md overflow-hidden">
<div className="h-48 bg-gray-200">
<img src="/product.jpg" className="w-full h-full object-cover" />
</div>
<div className="p-4">
<h3 className="text-lg font-semibold">Product Name</h3>
<p className="text-gray-600">$29.99</p>
</div>
</div>
</div>
I see h-48 controlling the image container height, but where's the width? I don't see any w- classes on the cards! How do they know how wide to be? π§
The Grid Width Revelationβ
Frontend Mentor: EXCELLENT question! π― You're thinking deeply about layoutβthis is advanced understanding!
Let me blow your mind: You DON'T set card width because the GRID does it for you! π€―
The Grid is the Boss of Widthβ
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
β β β β
These control card width automatically!
</div>
How it works:
- The grid parent says "I have X columns"
- The cards (children) automatically divide the available space
- Each card takes
100% Γ· number_of_columns - You never set width manually!
Me: Wait, so the grid just... calculates it for me?
Frontend Mentor: EXACTLY! The grid system is declarative, not manual. You declare "I want 3 columns" and the browser handles all the math! π
Width Calculation Breakdownβ
Frontend Mentor: Let me show you the math at each breakpoint:
π± Mobile (grid-cols-1)β
<div className="grid grid-cols-1 gap-6">
<div className="card">...</div>
</div>
Visual:
βββββββββββββββββββββββββββββββ
β Card 1 (100% width) β
βββββββββββββββββββββββββββββββ€
β Card 2 (100% width) β
βββββββββββββββββββββββββββββββ€
β Card 3 (100% width) β
βββββββββββββββββββββββββββββββ
Width calculation:
- 1 column β Each card takes
100% Γ· 1 = 100%of container width - If container is 375px (iPhone), each card is 375px wide
- Cards stack vertically, each taking full width
π± Tablet (sm:grid-cols-2)β
<div className="grid sm:grid-cols-2 gap-6">
<div className="card">...</div>
<div className="card">...</div>
</div>
Visual:
ββββββββββββββββββββ¬βββββββββββββββββββ
β Card 1 (50%) β Card 2 (50%) β
ββββββββββββββββββββΌβββββββββββββββββββ€
β Card 3 (50%) β Card 4 (50%) β
ββββββββββββββββββββ΄βββββββββββββββββββ
Width calculation:
- 2 columns β Each card takes
(100% - gap) Γ· 2 β 50%of container width - If container is 768px (iPad), each card is
(768px - 24px) Γ· 2 = 372px - The
gap-6(24px) is subtracted before division!
π» Desktop (md:grid-cols-3)β
<div className="grid md:grid-cols-3 gap-6">
<div className="card">...</div>
<div className="card">...</div>
<div className="card">...</div>
</div>
Visual:
ββββββββββββββ¬βββββββββββββ¬βββββββββββββ
β Card 1 β Card 2 β Card 3 β
β (~33%) β (~33%) β (~33%) β
ββββββββββββββΌβββββββββββββΌβββββββββββββ€
β Card 4 β Card 5 β Card 6 β
β (~33%) β (~33%) β (~33%) β
ββββββββββββββ΄βββββββββββββ΄βββββββββββββ
Width calculation:
- 3 columns β Each card takes
(100% - gaps) Γ· 3 β 33.33%of container width - If container is 1024px (laptop), each card is
(1024px - 48px) Γ· 3 β 325px - Two gaps (between 3 columns) =
gap-6 Γ 2 = 48px
π₯οΈ Large Desktop (lg:grid-cols-4)β
<div className="grid lg:grid-cols-4 gap-6">
<div className="card">...</div>
<div className="card">...</div>
<div className="card">...</div>
<div className="card">...</div>
</div>
Visual:
βββββββββββ¬ββββββββββ¬ββββββββββ¬ββββββββββ
β Card 1 β Card 2 β Card 3 β Card 4 β
β (25%) β (25%) β (25%) β (25%) β
βββββββββββΌββββββββββΌββββββββββΌββββββββββ€
β Card 5 β Card 6 β Card 7 β Card 8 β
β (25%) β (25%) β (25%) β (25%) β
βββββββββββ΄ββββββββββ΄ββββββββββ΄ββββββββββ
Width calculation:
- 4 columns β Each card takes
(100% - gaps) Γ· 4 = 25%of container width - If container is 1440px (desktop), each card is
(1440px - 72px) Γ· 4 = 342px - Three gaps (between 4 columns) =
gap-6 Γ 3 = 72px
The Gap Factor: Width Isn't Exactly 50%β
Me: Wait, you said (100% - gap) Γ· 2 for 2 columns. Why subtract the gap?
Frontend Mentor: Sharp eye! π The gap takes up space too!
Understanding Gap in Width Calculationβ
<div className="grid grid-cols-2 gap-6">
<div>Card 1</div>
<div>Card 2</div>
</div>
Total available space breakdown:
βββββββββββββββββββββββββββββββββββββββββββ
β [Card 1] [24px gap] [Card 2] β
β (?) (?) β
βββββββββββββββββββββββββββββββββββββββββββ
Container width: 100%
Gap space: 24px (gap-6)
Remaining for cards: 100% - 24px
Card 1 width: (100% - 24px) Γ· 2
Card 2 width: (100% - 24px) Γ· 2
Real numbers example:
Container: 768px
Gap: 24px
Remaining: 768px - 24px = 744px
Card 1: 744px Γ· 2 = 372px
Card 2: 744px Γ· 2 = 372px
Visual:
βββββββββββββββββββββββββββββββββββββββββββ
β [ββ 372px ββ] [24px] [ββ 372px ββ] β
β Card 1 gap Card 2 β
βββββββββββββββββββββββββββββββββββββββββββ
Total: 372 + 24 + 372 = 768px β
Multiple Gaps with More Columnsβ
3 columns = 2 gaps:
ββββββββββββββββββββββββββββββββββββββββββββββ
β [Card] [gap] [Card] [gap] [Card] β
β β β β
β gap-6 gap-6 β
ββββββββββββββββββββββββββββββββββββββββββββββ
Total gap space: 24px Γ 2 = 48px
Card width: (Container width - 48px) Γ· 3
4 columns = 3 gaps:
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β [Card] [gap] [Card] [gap] [Card] [gap] [Card] β
β β β β β
β gap-6 gap-6 gap-6 β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Total gap space: 24px Γ 3 = 72px
Card width: (Container width - 72px) Γ· 4
Complete Size Control: Who Controls What?β
Me: So grid controls width, but what about height?
Frontend Mentor: Great question! Let me break down the ENTIRE sizing hierarchy:
The Sizing Hierarchyβ
<div className="grid grid-cols-3 gap-6">
β Grid parent
Controls: Column count (which determines card width)
Does NOT control: Card height
<div className="card bg-white rounded-lg shadow-md overflow-hidden">
β Card (grid child)
Width: Automatic from grid (33.33% in this case)
Height: Grows with content (default behavior)
<div className="h-48 bg-gray-200">
β Image container
Width: Inherits from card (no explicit width set)
Height: Fixed at h-48 (192px)
<img src="/product.jpg" className="w-full h-full object-cover" />
β Image
Width: w-full (100% of image container)
Height: h-full (100% of image container = 192px)
Object-fit: object-cover (crops to fill container)
</div>
<div className="p-4">
β Content area
Width: Inherits from card
Height: Grows with content
</div>
</div>
</div>
Size Control Tableβ
| Element | Width Control | Height Control |
|---|---|---|
| Grid Parent | Determines columns | No control (flexible) |
| Card | Automatic from grid | Grows with content |
| Image Container | w-full (inherits card width) | h-48 (fixed 192px) |
| Image | w-full (fills container) | h-full (fills container) |
| Content Area | Inherits card width | Grows with content |
Key Insight Diagramβ
ββ Grid Container (width: 100%) ββββββββββββββββββ
β β
β ββ Card (width: 33.33% AUTO) ββββββββββββ β
β β β β
β β ββ Image Container (h-48) ββββββββββ β β
β β β Width: 100% of card β β β
β β β Height: 192px FIXED β β β
β β β β β β
β β β ββ Image (w-full h-full) ββ β β β
β β β β Fills container β β β β
β β β ββββββββββββββββββββββββββββ β β β
β β ββββββββββββββββββββββββββββββββββββ β β
β β β β
β β ββ Content (p-4) ββββββββββββββββββ β β
β β β Title, Price, etc. β β β
β β β Height: FLEXIBLE (content) β β β
β β ββββββββββββββββββββββββββββββββββββ β β
β β β β
β ββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Width Flow: Grid β Card (AUTO) β Image Container (inherit) β Image (w-full)
Height Flow: You set h-48 β Image Container β Image (h-full)
Why You DON'T Set Card Widthβ
Me: What if I DO set a width on the cards? Like w-64?
Frontend Mentor: Let me show you why that breaks the responsive design! π₯
Breaking the Grid with Fixed Widthβ
β DON'T DO THIS:
<div className="grid grid-cols-3 gap-6">
<div className="w-64 card"> {/* β Fixed 256px width */}
...
</div>
</div>
What happens:
Desktop (wide enough):
ββββββββββ¬βββββββββ¬βββββββββ
β 256px β 256px β 256px β
ββββββββββ΄βββββββββ΄βββββββββ
Lots of wasted space β [empty space]
Mobile (narrow):
ββββββββββ
β 256px β β Breaks out of container!
ββββββββββ Horizontal scroll appears! π±
Problems:
- β Cards don't fill available space on desktop (wasted space)
- β Cards overflow container on mobile (horizontal scrollbar)
- β Breaks responsive design completely
- β Not flexible or adaptive
The Grid Way (Correct)β
β
DO THIS:
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
<div className="card"> {/* β No width set! */}
...
</div>
</div>
What happens:
Desktop:
ββββββββββββββββ¬βββββββββββββββ¬βββββββββββββββ
β Flexible β Flexible β Flexible β
β fills 33% β fills 33% β fills 33% β
ββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββ
Perfect use of space! β¨
Mobile:
ββββββββββββββββββββββββββββββ
β Flexible β
β fills 100% β
ββββββββββββββββββββββββββββββ
Perfect fit! β¨
Benefits:
- β Cards automatically fill available space
- β Responsive at all screen sizes
- β No overflow or scrollbars
- β Flexible and adaptive
The w-full Pattern for Imagesβ
Me: But I DO see w-full on the image! Isn't that setting width?
Frontend Mentor: Aha! Great observation! w-full has a special meaning!
Understanding w-fullβ
<img src="/product.jpg" className="w-full h-full object-cover" />
β
"Fill my parent container"
What w-full means:
width: 100%(in CSS)- "Be as wide as my parent element"
- NOT a fixed width!
- Relative to parent container
The Flow of w-fullβ
<div className="grid grid-cols-3">
β Grid: "Each column is 33.33% of me"
<div className="card">
β Card: "I'm 33.33% of grid" (automatic)
<div className="h-48">
β Image container: "I'm 100% of card width" (default)
<img className="w-full h-full" />
β Image: "I'm 100% of image container" (w-full)
</div>
</div>
</div>
Width cascade:
Grid container: 1200px
β
Card: 1200px Γ· 3 = 400px (grid controls this)
β
Image container: 400px (inherits from card)
β
Image: 400px (w-full = 100% of 400px)
The key:
w-fullis relative (adapts to parent)- Card width is automatic (grid controls it)
- Result: Everything scales together! π
Practical Example: Complete Card Breakdownβ
Me: Can you show me a complete card with all the sizing explained?
Frontend Mentor: Absolutely! Here's a production card with full annotations:
Complete Product Cardβ
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 p-6">
{/* β Grid parent controls card widths automatically */}
<div className="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-xl transition-shadow">
{/* β Card wrapper - NO WIDTH SET! Grid controls it! */}
{/* Width: Auto (100% in mobile, 50% in tablet, 33% in desktop, 25% in large) */}
{/* Height: Grows with content */}
<div className="relative h-48 bg-gray-200">
{/* β Image container */}
{/* Width: Inherits from card (no explicit width needed) */}
{/* Height: Fixed at 192px (h-48) */}
<img
src="/product.jpg"
alt="Product"
className="w-full h-full object-cover"
/>
{/* β Image */}
{/* Width: w-full = 100% of image container (inherits card width) */}
{/* Height: h-full = 100% of image container = 192px */}
{/* object-cover: Crop to fill container maintaining aspect ratio */}
<div className="absolute top-2 right-2 bg-red-500 text-white px-2 py-1 rounded-md text-sm font-bold">
SALE
</div>
{/* β Badge - width auto-sizes to content */}
</div>
<div className="p-4">
{/* β Content area */}
{/* Width: Inherits from card */}
{/* Height: Grows with content (flexible) */}
<h3 className="text-lg font-semibold mb-2 line-clamp-2">
Wireless Bluetooth Headphones with Noise Cancelling
</h3>
{/* β Title - width fills parent, height grows with text */}
<p className="text-gray-600 text-sm mb-4 line-clamp-3">
High-quality audio with active noise cancellation and 30-hour battery life
</p>
{/* β Description - flexible height with line clamp */}
<div className="flex items-center justify-between">
{/* β Price row - width fills parent, height based on content */}
<div>
<span className="text-2xl font-bold text-blue-600">$79.99</span>
<span className="text-sm text-gray-500 line-through ml-2">$99.99</span>
</div>
<button className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition-colors">
Add to Cart
</button>
{/* β Button - width auto-sizes to content + padding */}
</div>
</div>
</div>
</div>
Size Flow Visualizationβ
ββ Grid Container (1200px) βββββββββββββββββββββββββββββββββββ
β β
β ββ Card 1 (400px AUTO) βββββ ββ Card 2 (400px AUTO) βββ β
β β β β β β
β β ββ h-48 (192px) ββββββββββ β ββ h-48 (192px) ββββββββ β
β β β Width: 400px (inherit)ββ β β Width: 400px ββ β
β β β ββ β β ββ β
β β β ββ w-full h-full ββββββ β β ββ w-full h-full ββββ β
β β β β img: 400Γ192px βββ β β β img: 400Γ192px βββ β
β β β βββββββββββββββββββββββ β β βββββββββββββββββββββ β
β β ββββββββββββββββββββββββββ β ββββββββββββββββββββββββ β
β β β β β β
β β ββ p-4 (Content) βββββββββ β ββ p-4 (Content) ββββββ β
β β β Width: 400px ββ β β Width: 400px ββ β
β β β Height: FLEXIBLE ββ β β Height: FLEXIBLE ββ β
β β β - Title ββ β β - Title ββ β
β β β - Description ββ β β - Description ββ β
β β β - Price & Button ββ β β - Price & Button ββ β
β β ββββββββββββββββββββββββββ β ββββββββββββββββββββββββ β
β β β β β β
β βββββββββββββββββββββββββββββ βββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Grid: md:grid-cols-3
Card widths: 400px EACH (1200px Γ· 3 = 400px)
Gaps: 24px between cards (gap-6)
Common Sizing Mistakesβ
Me: What mistakes should I avoid with grid sizing?
Frontend Mentor: Here are the classics! π¨
Mistake 1: Setting Fixed Width on Cardsβ
β WRONG:
<div className="grid grid-cols-3 gap-6">
<div className="w-64 card"> {/* Fixed 256px - breaks responsive! */}
...
</div>
</div>
β
RIGHT:
<div className="grid grid-cols-3 gap-6">
<div className="card"> {/* No width - grid handles it! */}
...
</div>
</div>
Mistake 2: Using max-w on Grid Childrenβ
β WRONG:
<div className="grid grid-cols-3 gap-6">
<div className="max-w-sm card"> {/* Limits width - defeats grid! */}
...
</div>
</div>
β
RIGHT:
<div className="grid grid-cols-3 gap-6 max-w-7xl mx-auto">
{/* β Put max-w on grid container! */}
<div className="card">
...
</div>
</div>
Mistake 3: Forgetting h- on Image Containerβ
β WRONG:
<div className="bg-gray-200"> {/* No height! */}
<img src="/product.jpg" className="w-full h-full" />
{/* Image has no height to fill! Invisible or tiny! */}
</div>
β
RIGHT:
<div className="h-48 bg-gray-200"> {/* Fixed height! */}
<img src="/product.jpg" className="w-full h-full object-cover" />
{/* Now image fills 192px height! */}
</div>
Mistake 4: Using w-full on Cardβ
β UNNECESSARY (but not harmful):
<div className="grid grid-cols-3 gap-6">
<div className="w-full card"> {/* Redundant! Already 100% of grid column */}
...
</div>
</div>
β
CLEANER:
<div className="grid grid-cols-3 gap-6">
<div className="card"> {/* Already fills grid column */}
...
</div>
</div>
Responsive Sizing in Actionβ
Me: Show me how card sizes change across breakpoints!
Frontend Mentor: Here's the magic! β¨
Real-World Responsive Exampleβ
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 p-6">
<div className="card">Product Card</div>
</div>
Actual card widths at different screen sizes:
| Screen Size | Breakpoint | Container Width | Columns | Gaps | Card Width |
|---|---|---|---|---|---|
| iPhone SE | 375px | 375px - 48px padding = 327px | 1 | 0 | 327px |
| iPad Mini | 768px | 768px - 48px padding = 720px | 2 | 24px | (720-24)/2 = 348px |
| Laptop | 1024px | 1024px - 48px padding = 976px | 3 | 48px | (976-48)/3 = 309px |
| Desktop | 1440px | 1440px - 48px padding = 1392px | 4 | 72px | (1392-72)/4 = 330px |
Visual progression:
π± Mobile (375px):
βββββββββββββββββββββββββ
β Card (327px) β
βββββββββββββββββββββββββ€
β Card (327px) β
βββββββββββββββββββββββββ
π± Tablet (768px):
ββββββββββββββββ¬βββββββββββββββ
β Card (348px) β Card (348px) β
ββββββββββββββββ΄βββββββββββββββ
π» Laptop (1024px):
βββββββββββ¬ββββββββββ¬ββββββββββ
β C(309px)β C(309px)β C(309px)β
βββββββββββ΄ββββββββββ΄ββββββββββ
π₯οΈ Desktop (1440px):
ββββββββββ¬βββββββββ¬βββββββββ¬βββββββββ
βC(330px)βC(330px)βC(330px)βC(330px)β
ββββββββββ΄βββββββββ΄βββββββββ΄βββββββββ
Key insight: Card widths adapt automatically to fill available space optimally!
Key Takeawaysβ
Me: Let me summarize what I learned about grid sizing!
Frontend Mentor: Perfect! Here's your complete guide:
The Golden Rulesβ
-
Grid controls width, you control height
- Grid parent: Sets column count β determines card width
- You: Set
h-48on image containers β controls height
-
Never set width on grid children
- β Don't use
w-64,w-96, etc. on cards - β Let grid calculate optimal width automatically
- β Don't use
-
Use w-full for images, not cards
- Cards: No width class needed
- Images:
w-fullto fill card width
-
Gaps reduce available space
- Card width =
(Container width - total gaps) Γ· columns - More columns = more gaps = slightly smaller cards
- Card width =
Size Control Hierarchyβ
ββ Grid Parent ββββββββββββββββββββββ
β Controls: Column count β
β Result: Card widths automatic β
β β
β ββ Card ββββββββββββββββββββββ β
β β Width: AUTO from grid β β
β β Height: Grows with content β β
β β β β
β β ββ Image Container ββββββββ β
β β β Width: Inherits ββ β
β β β Height: YOU SET (h-48) ββ β
β β β ββ β
β β β ββ Image ββββββββββββββ β
β β β β w-full h-full βββ β
β β β β Fills container βββ β
β β β βββββββββββββββββββββββ β
β β βββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββ
Quick Referenceβ
| Element | Width | Height |
|---|---|---|
| Grid | 100% (or max-w-*) | Auto (flexible) |
| Card | AUTO (grid sets it) | Auto (grows with content) |
| Image Container | Inherits from card | YOU SET (h-48, h-64, etc.) |
| Image | w-full (100% of container) | h-full (100% of container) |
Remember:β
"The grid is the boss of width!"
- Grid says "I have 3 columns"
- Cards automatically become 33.33% wide
- You never manually set card width
- Everything just works! β¨
Me: This is brilliant! I was looking for w- classes on my cards when the grid was handling it all along! Now I understand why I only see h-48 for heightβthat's the only dimension I need to control manually!
Frontend Mentor: Exactly! π― You've just leveled up your grid knowledge! The grid system is smart:
- Width: Grid calculates it (automatic, responsive, perfect)
- Height: You set it where needed (h-48 for images, flexible for text)
This is the beauty of CSS Gridβdeclarative layout that adapts automatically. No manual width calculations, no media query math, just clean, responsive design! π
Now go build those beautiful product grids with confidence! πͺ
Did you have similar "aha moments" with CSS Grid? Share your discoveries in the comments! ππ¬
