Skip to main content Skip to navigation Skip to search Skip to footer

📦 Product Components

Product cards, listings, galleries, and detailed product display components

🎭 Component Demonstrations

Interactive product components including cards, galleries, ratings, and inventory indicators

Product Cards

Various product card layouts for different display contexts

Wireless Headphones
20% OFF

Wireless Headphones

High-quality wireless headphones with noise cancellation

$79.99 $99.99
Bluetooth Speaker

Bluetooth Speaker

$149.99

Featured Product

Premium Smartphone Case

$24.99

Product Swatches

Color, size, and material selection components

T-Shirt Options

Color

Selected:

Size

Selected:

Material
Your Selection:

T-Shirt, Size , Material

Product Image Gallery

Interactive image galleries with zoom and navigation

Product Image
🔍 Hover to zoom

Product Images

Product Ratings

Rating displays and review components for customer feedback

Customer Reviews

4.5
★★★★☆
Based on 127 reviews
5★
65%
4★
25%
3★
8%
2★
2%

Recent Reviews

★★★★★
2 days ago
Amazing quality!
Great product, exactly as described. Fast shipping too.
- Sarah M.
★★★★☆
1 week ago
Good value
Works well, though the build quality could be better.
- Mike R.
★★★★★
2 weeks ago
Highly recommended
Perfect for my needs. Will definitely buy again.
- Jennifer L.

Stock Status Indicators

Visual indicators for product availability and inventory levels

Wireless Mouse

In Stock
25 units available

USB Drive

⚠️
Low Stock
Only 3 left!

Gaming Keyboard

Out of Stock
Expected: Next week

📝 Usage Examples

Code snippets for implementing product components in your Hyvä theme

Basic Product Card with Alpine.js

<div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow"
     x-data="{ isWishlisted: false }">
    <div class="relative">
        <img src="<?= $escaper->escapeUrl($product->getImageUrl()) ?>" 
             alt="<?= $escaper->escapeHtmlAttr($product->getName()) ?>" 
             class="w-full h-48 object-cover loading-lazy">
        <button @click="isWishlisted = !isWishlisted"
                class="absolute top-2 right-2 p-2 bg-white rounded-full shadow-md hover:bg-red-50"
                :class="isWishlisted ? 'text-red-500' : 'text-gray-400'">
            <span x-text="isWishlisted ? '♥' : '♡'"></span>
        </button>
    </div>
    <div class="p-4">
        <h3 class="font-semibold text-gray-900 mb-2"><?= $escaper->escapeHtml($product->getName()) ?></h3>
        <div class="flex items-center justify-between">
            <span class="text-fluid-xl font-bold text-gray-900">$<?= $escaper->escapeHtml($product->getPrice()) ?></span>
            <button class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors">
                Add to Cart
            </button>
        </div>
    </div>
</div>

Product Quick Actions (Wishlist, Compare)

<div class="flex space-x-2" x-data="{ 
    wishlist: [], 
    compare: [],
    addToWishlist(productId) {
        if (!this.wishlist.includes(productId)) {
            this.wishlist.push(productId);
        }
    },
    addToCompare(productId) {
        if (!this.compare.includes(productId) && this.compare.length < 3) {
            this.compare.push(productId);
        }
    }
}">
    <button @click="addToWishlist(<?= $product->getId() ?>)"
            :class="wishlist.includes(<?= $product->getId() ?>) ? 'text-red-500' : 'text-gray-400'"
            class="p-2 hover:bg-gray-100 rounded transition-colors"
            title="Add to Wishlist">
        ♡
    </button>
    
    <button @click="addToCompare(<?= $product->getId() ?>)"
            :class="compare.includes(<?= $product->getId() ?>) ? 'text-blue-500' : 'text-gray-400'"
            class="p-2 hover:bg-gray-100 rounded transition-colors"
            title="Add to Compare">
        ⚖️
    </button>
    
    <span x-show="compare.length > 0" 
          class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">
        Compare (<span x-text="compare.length"></span>)
    </span>
</div>

Product Rating and Review Integration

<div class="space-y-3" x-data="{ 
    rating: <?= $product->getRatingSummary() ?>,
    reviewCount: <?= $product->getReviewsCount() ?>,
    showReviews: false
}">
    <!-- Star Rating Display -->
    <div class="flex items-center space-x-2">
        <div class="flex text-yellow-400">
            <template x-for="star in 5" :key="star">
                <span x-text="star <= Math.floor(rating/20) ? '★' : '☆'"></span>
            </template>
        </div>
        <span class="text-sm text-gray-600" x-text="`(${reviewCount} reviews)`"></span>
    </div>
    
    <!-- Reviews Toggle -->
    <button @click="showReviews = !showReviews"
            class="text-blue-600 hover:text-blue-800 text-sm underline">
        <span x-text="showReviews ? 'Hide Reviews' : 'Show Reviews'"></span>
    </button>
    
    <!-- Reviews Section -->
    <div x-show="showReviews" x-transition class="border-t pt-3">
        <div class="space-y-2">
            <?php foreach ($block->getReviews() as $review): ?>
                <div class="p-3 bg-gray-50 rounded">
                    <div class="flex items-center justify-between mb-1">
                        <div class="flex text-yellow-400 text-xs">
                            <?= str_repeat('★', $review->getRating()) ?>
                            <?= str_repeat('☆', 5 - $review->getRating()) ?>
                        </div>
                        <span class="text-xs text-gray-500"><?= $review->getCreatedAt() ?></span>
                    </div>
                    <p class="text-sm text-gray-700"><?= $escaper->escapeHtml($review->getDetail()) ?></p>
                </div>
            <?php endforeach; ?>
        </div>
    </div>
</div>

⚡ Performance & Accessibility

Best practices for optimized and accessible product components

🎯

Product Accessibility

Implement proper image alt text, price formatting with currency symbols, ARIA labels for interactive elements, and keyboard navigation support for all product actions.

🚀

Performance Optimization

Enable image lazy loading with loading="lazy", use efficient Alpine.js data binding, implement virtual scrolling for large product lists, and optimize image formats (AVIF with JPEG fallback).

📱

Mobile-Friendly Interactions

Design touch-friendly buttons (minimum 44px target size), implement swipe gestures for image galleries, optimize for thumb navigation, and ensure readable text at mobile viewport sizes.

🔍

SEO Considerations

Add structured data markup (Product schema), implement semantic HTML5 elements, optimize meta descriptions and titles, and ensure proper heading hierarchy for search engine crawling.