feat: Add product images support with WP Media Library integration
- Add WP Media Library integration for product and variation images - Support images array (URLs) conversion to attachment IDs - Add images array to API responses (Admin & Customer SPA) - Implement drag-and-drop sortable images in Admin product form - Add image gallery thumbnails in Customer SPA product page - Initialize WooCommerce session for guest cart operations - Fix product variations and attributes display in Customer SPA - Add variation image field in Admin SPA Changes: - includes/Api/ProductsController.php: Handle images array, add to responses - includes/Frontend/ShopController.php: Add images array for customer SPA - includes/Frontend/CartController.php: Initialize WC session for guests - admin-spa/src/lib/wp-media.ts: Add openWPMediaGallery function - admin-spa/src/routes/Products/partials/tabs/GeneralTab.tsx: WP Media + sortable images - admin-spa/src/routes/Products/partials/tabs/VariationsTab.tsx: Add variation image field - customer-spa/src/pages/Product/index.tsx: Add gallery thumbnails display
This commit is contained in:
271
INLINE_SPACING_FIX.md
Normal file
271
INLINE_SPACING_FIX.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# Inline Spacing Fix - The Real Root Cause
|
||||
|
||||
## The Problem
|
||||
|
||||
Images were not filling their containers, leaving whitespace at the bottom. This was NOT a height issue, but an **inline element spacing issue**.
|
||||
|
||||
### Root Cause Analysis
|
||||
|
||||
1. **Images are inline by default** - They respect text baseline, creating extra vertical space
|
||||
2. **SVG icons create inline gaps** - SVGs also default to inline display
|
||||
3. **Line-height affects layout** - Parent containers with text create baseline alignment issues
|
||||
|
||||
### Visual Evidence
|
||||
|
||||
```
|
||||
┌─────────────────────┐
|
||||
│ │
|
||||
│ IMAGE │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────┘
|
||||
↑ Whitespace gap here (caused by inline baseline)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The Solution
|
||||
|
||||
### Three Key Fixes
|
||||
|
||||
#### 1. Make Images Block-Level
|
||||
```tsx
|
||||
// Before (inline by default)
|
||||
<img className="w-full h-full object-cover" />
|
||||
|
||||
// After (block display)
|
||||
<img className="block w-full h-full object-cover" />
|
||||
```
|
||||
|
||||
#### 2. Remove Inline Whitespace from Container
|
||||
```tsx
|
||||
// Add fontSize: 0 to parent
|
||||
<div style={{ fontSize: 0 }}>
|
||||
<img className="block w-full h-full object-cover" />
|
||||
</div>
|
||||
```
|
||||
|
||||
#### 3. Reset Font Size for Text Content
|
||||
```tsx
|
||||
// Reset fontSize for text elements inside
|
||||
<div style={{ fontSize: '1rem' }}>
|
||||
No Image
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation
|
||||
|
||||
### ProductCard Component
|
||||
|
||||
**All 4 layouts fixed:**
|
||||
|
||||
```tsx
|
||||
// Classic, Modern, Boutique, Launch
|
||||
<div className="relative w-full h-64 overflow-hidden bg-gray-100"
|
||||
style={{ fontSize: 0 }}>
|
||||
{product.image ? (
|
||||
<img
|
||||
src={product.image}
|
||||
alt={product.name}
|
||||
className="block w-full h-full object-cover object-center"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-gray-400"
|
||||
style={{ fontSize: '1rem' }}>
|
||||
No Image
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
```
|
||||
|
||||
**Key changes:**
|
||||
- ✅ Added `style={{ fontSize: 0 }}` to container
|
||||
- ✅ Added `block` class to `<img>`
|
||||
- ✅ Reset `fontSize: '1rem'` for "No Image" text
|
||||
- ✅ Added `flex items-center justify-center` to button with Heart icon
|
||||
|
||||
---
|
||||
|
||||
### Product Page
|
||||
|
||||
**Same fix applied:**
|
||||
|
||||
```tsx
|
||||
<div className="relative w-full h-96 rounded-lg overflow-hidden bg-gray-100"
|
||||
style={{ fontSize: 0 }}>
|
||||
{product.image ? (
|
||||
<img
|
||||
src={product.image}
|
||||
alt={product.name}
|
||||
className="block w-full h-full object-cover object-center"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-gray-400"
|
||||
style={{ fontSize: '1rem' }}>
|
||||
No image
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Why This Works
|
||||
|
||||
### The Technical Explanation
|
||||
|
||||
#### Inline Elements and Baseline
|
||||
- By default, `<img>` has `display: inline`
|
||||
- Inline elements align to the text baseline
|
||||
- This creates a small gap below the image (descender space)
|
||||
|
||||
#### Font Size Zero Trick
|
||||
- Setting `fontSize: 0` on parent removes whitespace between inline elements
|
||||
- This is a proven technique for removing gaps in inline layouts
|
||||
- Text content needs `fontSize: '1rem'` reset to be readable
|
||||
|
||||
#### Block Display
|
||||
- `display: block` removes baseline alignment
|
||||
- Block elements fill their container naturally
|
||||
- No extra spacing or gaps
|
||||
|
||||
---
|
||||
|
||||
## Files Modified
|
||||
|
||||
### 1. ProductCard.tsx
|
||||
**Location:** `customer-spa/src/components/ProductCard.tsx`
|
||||
|
||||
**Changes:**
|
||||
- Classic layout (line ~43)
|
||||
- Modern layout (line ~116)
|
||||
- Boutique layout (line ~183)
|
||||
- Launch layout (line ~247)
|
||||
|
||||
**Applied to all:**
|
||||
- Container: `style={{ fontSize: 0 }}`
|
||||
- Image: `className="block ..."`
|
||||
- Fallback text: `style={{ fontSize: '1rem' }}`
|
||||
|
||||
---
|
||||
|
||||
### 2. Product/index.tsx
|
||||
**Location:** `customer-spa/src/pages/Product/index.tsx`
|
||||
|
||||
**Changes:**
|
||||
- Product image container (line ~121)
|
||||
- Same pattern as ProductCard
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Visual Test
|
||||
1. ✅ Go to `/shop`
|
||||
2. ✅ Check product images - should fill containers completely
|
||||
3. ✅ No whitespace at bottom of images
|
||||
4. ✅ Hover effects should work smoothly
|
||||
|
||||
### Product Page Test
|
||||
1. ✅ Click any product
|
||||
2. ✅ Product image should fill container
|
||||
3. ✅ No whitespace at bottom
|
||||
4. ✅ Image should be 384px tall (h-96)
|
||||
|
||||
### Browser Test
|
||||
- ✅ Chrome
|
||||
- ✅ Firefox
|
||||
- ✅ Safari
|
||||
- ✅ Edge
|
||||
|
||||
---
|
||||
|
||||
## Best Practices Applied
|
||||
|
||||
### Global CSS Recommendation
|
||||
For future projects, add to global CSS:
|
||||
|
||||
```css
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
}
|
||||
```
|
||||
|
||||
This prevents inline spacing issues across the entire application.
|
||||
|
||||
### Why We Used Inline Styles
|
||||
- Tailwind doesn't have a `font-size: 0` utility
|
||||
- Inline styles are acceptable for one-off fixes
|
||||
- Could be extracted to custom Tailwind class if needed
|
||||
|
||||
---
|
||||
|
||||
## Comparison: Before vs After
|
||||
|
||||
### Before
|
||||
```tsx
|
||||
<div className="relative w-full h-64">
|
||||
<img className="w-full h-full object-cover" />
|
||||
</div>
|
||||
```
|
||||
**Result:** Whitespace at bottom due to inline baseline
|
||||
|
||||
### After
|
||||
```tsx
|
||||
<div className="relative w-full h-64" style={{ fontSize: 0 }}>
|
||||
<img className="block w-full h-full object-cover" />
|
||||
</div>
|
||||
```
|
||||
**Result:** Perfect fill, no whitespace
|
||||
|
||||
---
|
||||
|
||||
## Key Learnings
|
||||
|
||||
### 1. Images Are Inline By Default
|
||||
Always remember that `<img>` elements are inline, not block.
|
||||
|
||||
### 2. Baseline Alignment Creates Gaps
|
||||
Inline elements respect text baseline, creating unexpected spacing.
|
||||
|
||||
### 3. Font Size Zero Trick
|
||||
Setting `fontSize: 0` on parent is a proven technique for removing inline gaps.
|
||||
|
||||
### 4. Display Block Is Essential
|
||||
For images in containers, always use `display: block`.
|
||||
|
||||
### 5. SVGs Have Same Issue
|
||||
SVG icons also need `display: block` to prevent spacing issues.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Problem:** Whitespace at bottom of images due to inline element spacing
|
||||
|
||||
**Root Cause:** Images default to `display: inline`, creating baseline alignment gaps
|
||||
|
||||
**Solution:**
|
||||
1. Container: `style={{ fontSize: 0 }}`
|
||||
2. Image: `className="block ..."`
|
||||
3. Text: `style={{ fontSize: '1rem' }}`
|
||||
|
||||
**Result:** Perfect image fill with no whitespace! ✅
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
Thanks to the second opinion for identifying the root cause:
|
||||
- Inline SVG spacing
|
||||
- Image baseline alignment
|
||||
- Font-size zero technique
|
||||
|
||||
This is a classic CSS gotcha that many developers encounter!
|
||||
Reference in New Issue
Block a user