{# # ============================================================================ # ORGANISM: CHOICE CARD # ============================================================================ # # Carte de choix premium pour wizards et sélections. # Design avec icône, features list, gradient hover et flèche animée. # # ============================================================================ # COMPOSITION (Atomes/Molécules utilisés) # ============================================================================ # # - _atoms/icon/icon.html.twig # # ============================================================================ # PARAMÈTRES # ============================================================================ # # | Paramètre | Type | Défaut | Description | # |---------------|---------|-------------|---------------------------------------| # | title | string | (requis) | Titre de la carte | # | description | string | null | Description sous le titre | # | icon | string | null | Nom de l'icône principale | # | color | string | 'primary' | Couleur: primary, violet, warning... | # | colorHex | string | null | Couleur hex custom (ex: '#8B5CF6') | # | features | array | [] | Liste de features [{text: '...'}] | # | href | string | null | Lien (rend la carte cliquable) | # | action | string | null | data-action Stimulus | # | disabled | bool | false | Désactive la carte | # | badge | string | null | Badge texte (ex: 'Bientôt') | # | badgeIcon | string | null | Icône du badge | # | badgeHref | string | null | Rend le badge cliquable (ex: upgrade) | # | badgeVariant | string | 'muted' | 'muted' (gris) ou 'pro' (bleu coloré) | # | class | string | '' | Classes additionnelles | # # ============================================================================ # EXEMPLES D'UTILISATION # ============================================================================ # # Carte active: # {% include '_organisms/choice-card/choice-card.html.twig' with { # title: 'Importer un document', # description: 'Importez votre PDF et l\'IA analysera le contenu', # icon: 'document', # color: 'primary', # features: [ # { text: 'Structure automatique' }, # { text: 'Extraction des images' }, # { text: 'Génération de quiz' } # ], # action: 'click->wizard#selectPdf' # } only %} # # Carte désactivée: # {% include '_organisms/choice-card/choice-card.html.twig' with { # title: 'Rédiger manuellement', # description: 'Créez votre cours section par section', # icon: 'edit', # disabled: true, # badge: 'Bientôt', # badgeIcon: 'clock', # features: [ # { text: 'Contrôle total' }, # { text: 'Sans tokens' } # ] # } only %} # # ============================================================================ #} {% set title = title ?? '' %} {% set description = description ?? null %} {% set icon = icon ?? null %} {% set color = color ?? 'primary' %} {% set colorHex = colorHex ?? null %} {% set features = features ?? [] %} {% set href = href ?? null %} {% set action = action ?? null %} {% set disabled = disabled ?? false %} {% set badge = badge ?? null %} {% set badgeIcon = badgeIcon ?? null %} {% set badgeHref = badgeHref ?? null %} {% set badgeVariant = badgeVariant ?? 'muted' %} {% set class = class ?? '' %} {# Configuration des couleurs. La variante `primary` suit la COULEUR DE LA PAGE : ses fonds et ses bordures sont écrits sur `--color-primary`, que chaque outil surcharge sur son conteneur. Ses ombres, elles, étaient codées en dur sur le bleu de Moodia — d'où un halo bleu autour d'une tuile ambre sur Slidia. Elles passent donc par `--color-primary-rgb`, déclaré globalement dans assets/styles/variables.css et surchargé par les pages d'outil en même temps que `--color-primary` : les deux ne doivent jamais être dissociés. Même raison pour `arrowShadow` : l'utilitaire `shadow-primary` compile une valeur LITTÉRALE bleue (Tailwind v4 inline la valeur de thème), qu'aucune surcharge de variable CSS ne peut atteindre — c'est déjà documenté dans templates/slidia/index.html.twig. (Aucun commentaire Twig À L'INTÉRIEUR du littéral ci-dessous : le lexer n'y attend qu'une expression, un `{#` y produit une SyntaxError.) #} {% set colorConfig = { 'primary': { 'hex': '#0C81E4', 'iconBg': 'from-primary/15 to-primary/5', 'iconBorder': 'border-primary/20', 'iconShadow': 'shadow-[0_8px_32px_rgba(var(--color-primary-rgb),0.12)]', 'iconShadowHover': 'group-hover:shadow-[0_12px_40px_rgba(var(--color-primary-rgb),0.2)]', 'hoverBorder': 'hover:border-primary/30', 'hoverShadow': 'hover:shadow-[0_8px_40px_rgba(var(--color-primary-rgb),0.15)]', 'gradientBg': 'from-primary/5 via-transparent to-primary/10', 'arrowBg': 'bg-primary', 'arrowShadow': 'shadow-[0_10px_25px_rgba(var(--color-primary-rgb),0.3)]', 'checkCss': 'var(--color-primary)', 'titleHover': 'group-hover:text-primary', 'iconColor': 'primary' }, 'violet': { 'hex': '#8B5CF6', 'iconBg': 'from-[#8B5CF6]/15 to-[#8B5CF6]/5', 'iconBorder': 'border-[#8B5CF6]/20', 'iconShadow': 'shadow-[0_8px_32px_rgba(139,92,246,0.12)]', 'iconShadowHover': 'group-hover:shadow-[0_12px_40px_rgba(139,92,246,0.2)]', 'hoverBorder': 'hover:border-[#8B5CF6]/30', 'hoverShadow': 'hover:shadow-[0_8px_40px_rgba(139,92,246,0.15)]', 'gradientBg': 'from-[#8B5CF6]/5 via-transparent to-[#8B5CF6]/10', 'arrowBg': 'bg-[#8B5CF6]', 'arrowShadow': 'shadow-[#8B5CF6]/30', 'checkCss': '#8B5CF6', 'titleHover': 'group-hover:text-[#8B5CF6]', 'iconColor': '#8B5CF6' }, 'warning': { 'hex': '#F59E0B', 'iconBg': 'from-warning/15 to-warning/5', 'iconBorder': 'border-warning/20', 'iconShadow': 'shadow-[0_8px_32px_rgba(245,158,11,0.12)]', 'iconShadowHover': 'group-hover:shadow-[0_12px_40px_rgba(245,158,11,0.2)]', 'hoverBorder': 'hover:border-warning/30', 'hoverShadow': 'hover:shadow-[0_8px_40px_rgba(245,158,11,0.15)]', 'gradientBg': 'from-warning/5 via-transparent to-warning/10', 'arrowBg': 'bg-warning', 'arrowShadow': 'shadow-warning/30', 'checkCss': 'var(--color-warning)', 'titleHover': 'group-hover:text-warning', 'iconColor': 'warning' }, 'success': { 'hex': '#10B981', 'iconBg': 'from-success/15 to-success/5', 'iconBorder': 'border-success/20', 'iconShadow': 'shadow-[0_8px_32px_rgba(16,185,129,0.12)]', 'iconShadowHover': 'group-hover:shadow-[0_12px_40px_rgba(16,185,129,0.2)]', 'hoverBorder': 'hover:border-success/30', 'hoverShadow': 'hover:shadow-[0_8px_40px_rgba(16,185,129,0.15)]', 'gradientBg': 'from-success/5 via-transparent to-success/10', 'arrowBg': 'bg-success', 'arrowShadow': 'shadow-success/30', 'checkCss': 'var(--color-success)', 'titleHover': 'group-hover:text-success', 'iconColor': 'success' } } %} {% set cfg = colorConfig[color] ?? colorConfig['primary'] %} {# Déterminer le tag et les classes #} {% set isInteractive = not disabled and (href or action) %} {% set tag = href ? 'a' : (action ? 'button' : 'div') %} {# Classes de base #} {% set baseClasses = 'group relative bg-white rounded-3xl border border-gray-100 p-10 text-left overflow-hidden shadow-[0_4px_24px_rgba(0,0,0,0.04)] flex flex-col h-full' %} {% if isInteractive %} {% set interactiveClasses = 'transition-all duration-500 ease-out ' ~ cfg.hoverBorder ~ ' ' ~ cfg.hoverShadow ~ ' hover:-translate-y-2 focus:outline-none focus:ring-4 focus:ring-primary/20' %} {% else %} {# Pas d'opacité sur la CARTE (sinon le badge coloré serait atténué) : le contenu est estompé plus bas, le badge « pro » reste plein et coloré. #} {% set interactiveClasses = 'cursor-not-allowed' %} {% endif %} {# ============================================================================ RENDU HTML ============================================================================ #} <{{ tag }} {% if tag == 'a' and href %}href="{{ href }}"{% endif %} {% if tag == 'button' %}type="button"{% endif %} {% if action %}data-action="{{ action }}"{% endif %} class="{{ baseClasses }} {{ interactiveClasses }} {{ class }}" > {# Gradient background on hover #} {% if isInteractive %}
{# Flèche en haut à droite au hover #}{{ description }}
{% endif %} {# Features list — UNE seule coche pour toutes les cartes, nue et grande (20 px), dans l'esprit de la croix de fermeture unifiée (_atoms/close-button) : plus lisible, rien autour. Seule sa couleur change, via `cfg.checkCss`. C'est un SVG écrit ici, et NON l'atome icon : l'icône `check` du jeu Solar est un « check-circle » BOLD-DUOTONE — un disque plein en couche secondaire, plus la coche en couche primaire. Elle produisait donc la pastille colorée que l'on croyait venir d'un fond CSS, et qui subsistait après le retrait de ce fond. Seule la carte violette y échappait, parce qu'elle rendait déjà ce SVG à la main : d'où deux styles de coche côte à côte sur la même page. Retoucher l'atome est exclu, il sert partout dans l'application. `items-start` + `mt-px` et non `items-center` : sur un libellé qui passe à la ligne, une coche centrée verticalement flotte au milieu du bloc de texte au lieu de marquer sa première ligne. #} {% if features|length > 0 %} {% set checkCss = disabled ? '#9CA3AF' : cfg.checkCss %}