<div class="flex flex-col space-y-4 ">
<a href="#" class="block relative aspect-square overflow-hidden bg-gray-100 rounded-lg group hover:after:!h-0 noAbsolute">
<div class="relative w-full h-full">
<img src="/img/hero/videoLeft.jpg" alt="Product Name" class="w-full h-full object-cover transition-all duration-300 absolute inset-0 group-hover:opacity-0 ">
<img src="/img/hero/videoRight.jpg" alt="Product Name - Vue alternative" class="w-full h-full object-cover transition-all duration-300 absolute inset-0 opacity-0 group-hover:opacity-100">
</div>
<div class="absolute bottom-4 right-1/2 flex space-x-4 translate-x-2/4 duration-300 z-20">
</div>
</a>
<div class="px-2">
<p class="text-xxs font-semibold text-neutral-500">Brand Name</p>
<h3 class="font-semibold text-neutral-800 pt-0.5">Product Name</h3>
<p class="text-xs text-neutral-500 pt-1">Product description goes here with some details about the item.</p>
<div class="pt-3">
<p class="text-[9px] text-neutral-500">Prix web</p>
<div class="flex items-center gap-2">
<p class="font-semibold text-neutral-800">
99.99 €
</p>
</div>
</div>
</div>
</div>
{# components/slide/slideCard.twig #}
<div
class="flex flex-col space-y-4 {{ class }}"
>
<a
href="{{ slide.url }}"
class="block relative aspect-square overflow-hidden bg-gray-100 rounded-lg group hover:after:!h-0 noAbsolute"
{% if slide.openInNewTab %}target="_blank"{% endif %}
>
<div class="relative w-full h-full">
<img
src="{{ slide.image }}"
alt="{{ slide.name }}"
class="w-full h-full object-cover transition-all duration-300 absolute inset-0 {% if slide.hoverImage is defined and slide.hoverImage %} group-hover:opacity-0 {% endif %}"
>
{% if slide.hoverImage is defined and slide.hoverImage %}
<img
src="{{ slide.hoverImage }}"
alt="{{ slide.name }} - Vue alternative"
class="w-full h-full object-cover transition-all duration-300 absolute inset-0 opacity-0 group-hover:opacity-100"
>
{% endif %}
</div>
<div class="absolute bottom-4 right-1/2 flex space-x-4 translate-x-2/4 duration-300 z-20">
{% if slide.showActionButtons is not defined or slide.showActionButtons %}
{% if slide.showFavoriteButton is not defined or slide.showFavoriteButton %}
{% render "@template-button" with {
color: slide.buttonColor ~ "-ghost",
size: "sm",
type: "only-icon",
icon: {
name: "library--like-outline"
},
button_class: "group-hover:!btn-" ~ slide.buttonColorHover ~ "-ghost"
} %}
{% endif %}
{% if slide.showQuickViewButton is not defined or slide.showQuickViewButton %}
{% render "@template-button" with {
color: "light-ghost",
size: "sm",
type: "only-icon",
icon: {
name: "library--camera"
},
button_class: "group-hover:!btn-dark-ghost"
} %}
{% endif %}
{% endif %}
</div>
</a>
<div class="px-2">
{% if slide.brand is defined and slide.brand %}
<p class="text-xxs font-semibold text-neutral-500" {{ slide.alpineAttribute.slideInfo.brand }}>{{ slide.brand }}</p>
{% endif %}
{% if slide.inlinePrice is defined and slide.inlinePrice %}
<div class="flex justify-between items-end py-2">
{% if slide.name is defined and slide.name %}
<h3 class="font-semibold text-neutral-800 pt-0.5" {{ slide.alpineAttribute.slideInfo.name }}>{{ slide.name }}</h3>
{% endif %}
<div class="flex flex-col text-right">
{% if slide.webLabel is defined and slide.webLabel %}
<span class="text-[9px] text-neutral-500 leading-[0] uppercase" {{ slide.alpineAttribute.slideInfo.webLabel }}>{{ slide.webLabel }}</span>
{% endif %}
{% if slide.price is defined and slide.price %}
<span class="font-semibold text-neutral-800" {{ slide.alpineAttribute.slideInfo.price }}>
{{ slide.price }} €
</span>
{% endif %}
</div>
</div>
{% else %}
{% if slide.name is defined and slide.name %}
<h3 class="font-semibold text-neutral-800 pt-0.5" {{ slide.alpineAttribute.slideInfo.name }}>{{ slide.name }}</h3>
{% endif %}
{% endif %}
{% if slide.description is defined and slide.description %}
<p class="text-xs text-neutral-500 pt-1" {{ slide.alpineAttribute.slideInfo.description }}>{{ slide.description }}</p>
{% endif %}
{% if slide.colors is defined and slide.colors %}
<p class="text-xs text-neutral-500 pt-2" {{ slide.alpineAttribute.slideInfo.colors }}>{{ slide.colors }}</p>
{% endif %}
{% if not (slide.inlinePrice is defined and slide.inlinePrice) %}
<div class="pt-3">
<p class="text-[9px] text-neutral-500" {{ slide.alpineAttribute.slideInfo.webLabel }}>{{ slide.webLabel }}</p>
<div class="flex items-center gap-2">
<p class="font-semibold text-neutral-800" {{ slide.alpineAttribute.slideInfo.price }}>
{{ slide.price }} €
</p>
{% if slide.oldPrice and slide.discountText %}
<p class="text-neutral-500 text-xs line-through">
{{ slide.oldPrice }} €
</p>
<p class="text-green-700 text-xs">
{{ slide.discountText }}
</p>
{% endif %}
</div>
</div>
{% endif %}
{% if slide.addCart is defined and slide.addCart %}
<div class="flex justify-center">
{% render "@template-button" with {
label: "Ajouter au panier",
color: "dark-ghost",
size:"sm"
} %}
</div>
{% endif %}
</div>
</div>
{
"slide": {
"id": "123",
"url": "#",
"image": "/img/hero/videoLeft.jpg",
"hoverImage": "/img/hero/videoRight.jpg",
"brand": "Brand Name",
"name": "Product Name",
"description": "Product description goes here with some details about the item.",
"color": "3 couleurs disponibles",
"webLabel": "Prix web",
"price": "99.99",
"openInNewTab": false,
"showActionButtons": false,
"showFavoriteButton": true,
"showQuickViewButton": true,
"class": "",
"buttonColor": "light",
"buttonColorHover": "dark"
}
}
The Product Card component is used to display product information in a standardized layout. It is particularly suited for product grids, carousels, and recommendation lists.
The component displays:
meta: {
layout_wrapper_class: 'inline-grid grid-cols-1 md:grid-cols-3 xl:grid-cols-6 gap-10'
}
This configuration displays product cards in a responsive grid:
Prop | Type | Required | Default | Description |
---|---|---|---|---|
id | string | Yes | - | Unique product identifier |
url | string | Yes | ‘#’ | URL of the product page |
image | string | Yes | - | URL of the main image |
hoverImage | string | No | null | URL of the image displayed on hover |
brand | string | Yes | - | Brand name |
name | string | Yes | - | Product name |
description | string | No | - | Short product description |
color | string | No | - | Text indicating available colors |
webLabel | string | No | - | Price label (e.g., “Web Price”) |
price | string | Yes | - | Product price |
openInNewTab | boolean | No | false | Opens the link in a new tab |
showActionButtons | boolean | No | true | Enables/disables all action buttons |
showFavoriteButton | boolean | No | true | Enables/disables the favorite button |
showQuickViewButton | boolean | No | true | Enables/disables the quick view button |
class | string | No | “” | Additional CSS classes |
context: {
product: {
id: '123',
url: '#',
image: '/img/hero/videoLeft.jpg',
hoverImage: '/img/hero/videoRight.jpg',
brand: 'Brand Name',
name: 'Product Name',
description: 'Product description goes here with some details about the item.',
color: '3 colors available',
webLabel: 'Web Price',
price: '99.99',
openInNewTab: false,
showActionButtons: true,
showFavoriteButton: true,
showQuickViewButton: true,
class: ""
}
}
{% render "@product-card" with { product: product } %}
{% render "@product-card--without-buttons" with { product: product } %}
{% render "@product-card" with {
product: {
...product,
class: "my-custom-class"
}
} %}
Hover Image
hoverImage
is definedhoverImage
, a zoom effect is applied to the main imageAction Buttons
showActionButtons
is trueshowFavoriteButton
and showQuickViewButton
Navigation
openInNewTab