Button

Déclenche une action utilisateur. Quatre variantes, chacune avec une hiérarchie et un usage précis. La variante critical porte des règles comportementales spéciales pour les actions irréversibles. Triggers a user action. Four variants, each with a precise hierarchy and use case. The critical variant carries special behavioral rules for irreversible actions.

VariantesVariants

Primary — action principale, 1 maximum par sectionPrimary — main action, 1 maximum per section
Enregistrer les modificationsSave changes Enregistrer (désactivé)Save (disabled)
Secondary — action alternativeSecondary — alternative action
AnnulerCancel Annuler (désactivé)Cancel (disabled)
Ghost — action tertiaire, faible emphaseGhost — tertiary action, low emphasis
En savoir plusLearn more En savoir plus (désactivé)Learn more (disabled)
Critical — action irréversible (confirmation obligatoire)Critical — irreversible action (confirmation required)
Supprimer définitivementDelete permanently

IcônesIcons

Approche hybride : propriété (Figma, React, tous frameworks) ou slot (composition avancée). Le contenu slotté a toujours la priorité sur la propriété. Hybrid approach: property (Figma, React, all frameworks) or slot (advanced composition). Slotted content always takes priority over the property.

Icône avant (prefix)Leading icon (prefix)
AjouterAdd TéléchargerDownload ParamètresSettings
Icône après (suffix)Trailing icon (suffix)
ContinuerContinue ExporterExport
Icon-only — label obligatoire (WCAG 1.1.1)Icon-only — label required (WCAG 1.1.1)
<!-- Propriété icon — Figma Code Connect, React et tous les frameworks -->
<agtc-button icon="plus">Ajouter un élémentAdd item</agtc-button>
<agtc-button variant="secondary" icon-suffix="arrow-right">ContinuerContinue</agtc-button>

<!-- Icon-only — label="" obligatoire -->
<agtc-button icon-only icon="x" label="Fermer le panneauClose panel"></agtc-button>

<!-- Slot — composition avancée, SVG custom -->
<agtc-button>
  <agtc-icon slot="prefix" name="plus"></agtc-icon>
  Ajouter un élémentAdd item
</agtc-button>

Règles absoluesAbsolute rules

Tokens de composantComponent tokens

Token CSSRéférence sémantiqueSemantic referenceValeur résolueResolved value
--agtc-component-button-primary-backgroundsemantic.color.action.primary#007a68
--agtc-component-button-primary-background-hoversemantic.color.action.primary-hover#0d3d38
--agtc-component-button-primary-background-disabledsemantic.color.action.primary-disabled#d9d9d9
--agtc-component-button-primary-textsemantic.color.text.on-action#ffffff
--agtc-component-button-primary-padding-xsemantic.space.control.padding-x16px
--agtc-component-button-primary-padding-ysemantic.space.control.padding-y8px
--agtc-component-button-primary-radiussemantic.radius.control6px
--agtc-component-button-critical-backgroundsemantic.color.feedback.danger#ce2c31
--agtc-component-button-secondary-textsemantic.color.action.primary#007a68
--agtc-component-button-secondary-bordersemantic.color.action.primary#007a68
--agtc-component-button-ghost-textsemantic.color.action.primary#007a68

AccessibilitéAccessibility

Règles spéciales — variante criticalSpecial rules — critical variant

Token component.button.critical.$metadata.requires-confirmation = true. Avant d'utiliser cette variante, vérifier :Before using this variant, verify:

  1. Le pattern de confirmation existe dans l'interface (modale, popconfirm)The confirmation pattern exists in the UI (modal, popconfirm)
  2. Le libellé décrit l'action — ex: "Supprimer définitivement le dossier"The label describes the action — e.g. "Delete folder permanently"
  3. Le contraste est ≥ 4.5:1 sur fond blancContrast is ≥ 4.5:1 on white background
  4. L'agent escalade à un humain si le caractère irréversible de l'action n'est pas certainThe agent escalates to a human if the irreversibility of the action is uncertain

Compatibilité frameworksFramework compatibility

Frameworkicon="..."slot="prefix"Figma Code Connect
HTML natif
React 19
React 18⚠️ via refvia ref
Angular
Vue 3
Svelte

Implémentation — Lit Web ComponentImplementation — Lit Web Component

import { LitElement, html, css } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
// agtc-icon doit être enregistré par le consommateur pour les propriétés icon/icon-suffix
// import './agtc-icon.js';

class AgtcButton extends LitElement {
  static properties = {
    variant:    { type: String,  reflect: true }, // 'primary'|'secondary'|'ghost'|'critical'
    disabled:   { type: Boolean, reflect: true },
    loading:    { type: Boolean, reflect: true },
    iconOnly:   { type: Boolean, reflect: true, attribute: 'icon-only' },
    icon:       { type: String },                 // nom d'icône Lucide (prefix)
    iconSuffix: { type: String,  attribute: 'icon-suffix' }, // nom d'icône (suffix)
    label:      { type: String },                 // aria-label — obligatoire pour icon-only
    loadingLabel: { type: String, attribute: 'loading-label' },
  };

  render() {
    const busy = this.loading;
    return html`
      <button
        class="${[this.variant, busy ? 'loading' : '', this.iconOnly ? 'icon-only' : ''].filter(Boolean).join(' ')}"
        ?disabled="${this.disabled || busy}"
        aria-busy="${busy}"
        aria-label="${ifDefined(this.label ? (busy ? this.loadingLabel : this.label) : undefined)}"
      >
        <span class="spinner" aria-hidden="true"></span>
        <span class="content">
          <slot name="prefix">
            ${this.icon ? html`<agtc-icon name="${this.icon}" size="control"></agtc-icon>` : ''}
          </slot>
          <slot></slot>
          <slot name="suffix">
            ${this.iconSuffix ? html`<agtc-icon name="${this.iconSuffix}" size="control"></agtc-icon>` : ''}
          </slot>
        </span>
      </button>
    `;
  }
}
customElements.define('agtc-button', AgtcButton);

DOs et DON'TsDOs and DON'Ts

À faireDo

  • Libellé explicite décrivant l'action (« Supprimer définitivement le dossier »)Explicit label describing the action ("Delete folder permanently")
  • Maximum 1 bouton primary par section ou formulaireMaximum 1 primary button per section or form
  • Toujours un :focus-visible visible pour la navigation clavierAlways a visible :focus-visible for keyboard navigation
  • État loading avec aria-busy="true" pour les actions asynchronesloading state with aria-busy="true" for async actions
  • Pattern de confirmation obligatoire avant chaque action criticalMandatory confirmation pattern before each critical action

À éviterDon't

  • Libellé vague : « OK », « Confirmer », « Valider » seulVague label: "OK", "Confirm", "Submit" alone
  • Deux boutons primary côte à côte dans le même formulaireTwo primary buttons side by side in the same form
  • Couleurs ou espacements en dur (style="background:red")Hardcoded colors or spacing (style="background:red")
  • Variantes inventées hors de tokens/component.jsonInvented variants outside tokens/component.json
  • Bouton critical sans pattern de confirmationcritical button without a confirmation pattern

Anti-patternsAnti-patterns

MauvaisBadPourquoiWhy
<button style="background:red">Supprimer</button>Valeur en dur, variante non reconnue, pas de tokenHardcoded value, unrecognized variant, no token
<agtc-button variant="critical">OK</agtc-button>Libellé non explicite pour une action critiqueNon-explicit label for a critical action
Deux variant="primary" dans le même formulaireTwo variant="primary" in the same formHiérarchie cassée — perte de clarté UXBroken hierarchy — loss of UX clarity
<agtc-button variant="danger">Variante inexistante — escalader, demander la variante correcteNon-existent variant — escalate, ask for the correct variant

UX Patterns Reference

Patterns approved via the ux-pattern-review workflow (ADR-036). Decision: all approved.

PatternSourceAppliedJustification
A single clear primary action per contextIxDF — clear primary actionReadable action hierarchy
Explicit confirmation for destructive action (critical)NN/g — error preventionrequires-confirmation
Width preserved during loadingSmashingNo layout jump
Never disable without indicating why (prefer a motivated disabled over hiding the action)Smashing — hidden vs disabledA silent disabled is an accessibility anti-pattern — provide help text/tooltip explaining the block
Label describing the consequence, not "OK"NN/gExplicit label
Contribuer à ce projet Contribute to this project

Ce système est ouvert aux contributions — tokens, composants, décisions architecturales, corrections d'accessibilité ou documentation. Toute amélioration est bienvenue. This system welcomes contributions — tokens, components, architectural decisions, accessibility fixes, or documentation. Every improvement counts.

Voir sur GitHub View on GitHub