<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/productSolaire/img_5.png" alt="Pochette Verte" class="w-full h-full object-cover transition-all duration-300 absolute inset-0 ">
        </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">

        <div class="flex justify-between items-end py-2">
            <h3 class="font-semibold text-neutral-800 pt-0.5">Pochette Verte</h3>
            <div class="flex flex-col text-right">
                <span class="text-[9px] text-neutral-500 leading-[0] uppercase">PRIX WEB</span>

                <span class="font-semibold text-neutral-800">
                    9,00 €
                </span>
            </div>

        </div>

        <div class="flex justify-center">
            <button type="button" class=" btn btn-dark-ghost btn-size-sm">
                Ajouter au panier

            </button>
        </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/productSolaire/img_5.png",
    "hoverImage": false,
    "brand": false,
    "name": "Pochette Verte",
    "description": false,
    "color": "3 couleurs disponibles",
    "webLabel": "PRIX WEB",
    "price": "9,00",
    "openInNewTab": false,
    "showActionButtons": false,
    "showFavoriteButton": true,
    "showQuickViewButton": true,
    "class": "",
    "buttonColor": "light",
    "buttonColorHover": "dark",
    "inlinePrice": true,
    "addCart": true
  },
  "title": "Compléter avec...",
  "titleClass": "text-xl",
  "showCTA": false
}

Product Card

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.

Overview

The component displays:

  • A main image with hover effect
  • The product brand
  • The product name
  • A description
  • Available color options
  • Web label (e.g., “Web Price”)
  • The price
  • Optional action buttons (favorites and quick view)

Configuration

Layout Wrapper

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:

  • 1 column on mobile
  • 3 columns on tablets
  • 6 columns on desktops

Props

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

Standard Configuration Example

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: ""
  }
}

Usage

Basic

{% render "@product-card" with { product: product } %}

With Variant

{% render "@product-card--without-buttons" with { product: product } %}

With Custom Classes

{% render "@product-card" with { 
    product: {
        ...product,
        class: "my-custom-class"
    }
} %}

Behavior

  1. Hover Image

    • On hovering over the card, an alternate image fades in if hoverImage is defined
    • Without hoverImage, a zoom effect is applied to the main image
  2. Action Buttons

    • Buttons are positioned at the bottom of the image
    • Visible only if showActionButtons is true
    • Each button can be individually controlled with showFavoriteButton and showQuickViewButton
  3. Navigation

    • Clicking on the card redirects to the product URL
    • Option to open in a new tab via openInNewTab