Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Accessibilité

Composant IconComponent – Documentation d’usage

Objectif

Le composant IconComponent est un composant Angular standalone, conçu pour afficher des icônes SVG issues de la librairie Lucide (stockées en local dans assets/icons/lucide/).
Il est utilisé dans le design system Pecunia, pour gérer toutes les icônes d’interface : boutons, menus, statuts, badges, etc.


Définition

export class IconComponent {
  private readonly http = inject(HttpClient);

  //déclaration des signaux => mini varialbles observables
  readonly _name = signal<string>('');
  readonly _size = signal<IconSize>('md');
  readonly _ariaLabel = signal<string>('');
  readonly _isDecorative = signal<boolean>(false);

  //setter pour mettre à jour les inputs avec des signaux
  @Input({ required: true }) set name(value: string) {
    this._name.set(value);
  }
  @Input() set size(value: IconSize) {
    this._size.set(value);
  }
  @Input() set ariaLabel(value: string) {
    this._ariaLabel.set(value);
  }
  @Input() set isDecorative(value: boolean) {
    this._isDecorative.set(value);
  }

Ce composant est totalement réactif (via signal() et computed()), encapsulé, accessible et personnalisable.


Propriétés disponibles (@Input())

PropTypeObligatoireDescription
namestring✅ OuiNom du fichier SVG (ex: "plus", "arrow-left")
size'xs' | 'sm' | 'md' | 'lg'❌ NonTaille logique, applique une classe CSS (icon-size-md par défaut)
ariaLabelstring❌ NonTexte alternatif (accessibilité), utilisé si isDecorative = false
isDecorativeboolean❌ NonSi true, l’icône est masquée des lecteurs d’écran

Cas d’usages typiques

  • Icône seule dans un bouton d’action (delete, edit…)
  • Icône dans une puce, un badge ou une ligne de tableau
  • Icône décorative dans une UI (à cacher aux lecteurs d’écran)

Icônes décoratives vs informatives

Une icône est décorative si :

  • Elle n’ajoute aucune information essentielle
  • Elle accompagne un texte déjà explicite
  • Elle est utilisée uniquement pour améliorer l’esthétique

Exemples décoratifs :

  • Un 🔒 à côté du mot « Connexion »
  • Une icône 🛒 dans un bouton « Ajouter au panier »
  • Un pictogramme 🎯 dans une carte qui a déjà un titre

➡️ Accessibilité :

  • Utiliser [isDecorative]="true" dans Pecunia (ce qui appliquera aria-hidden="true")
  • Pas besoin de ariaLabel pour les icônes décoratives

Une icône est informative si :

  • Elle remplace un texte
  • Elle transmet une information visuelle (état, action)
  • Elle est la seule information visible

Exemples informatifs :

  • Une 🗑️ seule dans un bouton ➜ signifie “Supprimer”
  • Une icône ❗ dans un message ➜ signifie “Erreur”
  • Une 👁️ dans un champ ➜ signifie “Afficher le mot de passe”

➡️ Accessibilité :

  • Option 1 : Fournir un ariaLabel="Description" directement sur l’icône
  • Option 2 : Mettre aria-label sur l’élément parent (si l’icône est dans un élément interactif)

📝 Règle simple à retenir

Si l’icône peut être retirée sans perte d’information, elle est décorative.
Sinon, elle est informative et doit être accessible aux lecteurs d’écran.


Exemples concrets

  • Icône significative – Option 1 (label sur l’icône)
<app-ui-icon name="trash" ariaLabel="Supprimer la transaction"></app-ui-icon>
  • Icône significative – Option 2 (label sur le parent)
<button aria-label="Supprimer la transaction">
  <app-ui-icon name="trash" [isDecorative]="true"></app-ui-icon>
</button>
  • Icône décorative
<button>
  <app-ui-icon name="trash" [isDecorative]="true"></app-ui-icon>
  Supprimer la transaction
</button>

Cas d’usage dans le IconComponent

Situationaria labeldecorative
Icône seule dans un bouton"Supprimer"false
Icône accompagnée d’un texte""true
Icône de statut (succès, erreur…)"Succès"false
Icône purement esthétique""true

Comment ça marche ?

Le composant :

  • Résout le chemin SVG via un computed() :
    assets/icons/lucide/${name}.svg
  • Applique une classe de taille (icon-size-md, icon-size-lg, etc.)
  • Réagit à une erreur de chargement avec un fallback (alert-circle.svg)

Bonnes pratiques d’intégration

  • Toujours utiliser ce composant plutôt que des balises <img> ou <svg> brutes
  • Ne pas hardcoder le chemin de l’icône dans les composants parents
  • Préférer les tailles logiques (sm, md, etc.) au lieu de fixer les pixels
  • Respecter la séparation : le style (.scss) gère la taille réelle

Gestion couleur de l’icon

L’icône est affichée via un <span> contenant une mask-image SVG.
La couleur est appliquée via background-color: currentColor en CSS.

Pourquoi utiliser mask-image au lieu de img?

L’utilisation de mask-image offre plusieurs avantages importants :

  • Héritage de couleur : Une icône avec mask-image peut hériter de la couleur du texte parent via currentColor, ce qui est difficile avec des images SVG classiques.
  • Performance : Les masks CSS sont plus performants que les SVG injectés dans le DOM pour de nombreuses icônes.
  • Flexibilité : On peut changer la couleur dynamiquement sans modifier le fichier SVG source

Important : coloration pour les icônes sans texte

Pour les boutons ou éléments ne contenant que des icônes, il est impératif de définir la propriété CSS color dans le parent :

<!-- Composant bouton icône sans texte -->
<button class="icon-only-button">
  <app-icon name="trash" ariaLabel="Supprimer" />
</button>
// SCSS du composant parent
.icon-only-button {
  // IMPORTANT : définir la couleur même sans texte !
  @include theme.themed-block(
    (
      color: 'text-neutral-default',
      // Couleur pour l'icône
    )
  );
}

Pour le dossier CDA

Le composant IconComponent respecte les principes du design system :

  • réutilisable, autonome, testable
  • conforme aux bonnes pratiques d’accessibilité
  • basé sur la nouvelle API Angular signal() / computed() pour plus de lisibilité

Il est centralisé dans shared/ et documenté pour permettre son adoption par l’ensemble de l’équipe.