Tutoriel Angular

Composants autonomes Angular 20 — entrées signal, détection des changements OnPush, modèles en ligne, zéro NgModules, zéro CSS.

Ce que vous allez construire

Un petit formulaire de contact — champ avec étiquette, bouton d'envoi, liaison bidirectionnelle, vrai comportement clavier et lecteur d'écran — d'abord nu, puis stylé, en 10 minutes environ.

Avant de commencer

Il vous faut git, Node 22 ou plus récent et pnpm 10 ou plus récent. Tout projet Angular 20 convient — Angular CLI ou Analog.

Étape 1 — Récupérer le code

git clone https://github.com/LilyDesignSystem/lily-design-system
cd lily-design-system/lily-design-system-angular-headless
pnpm install
pnpm test        # vitest + TestBed, all 571 components
pnpm run build-storybook

Les composants sont des fichiers à plat dans components/ — Button.ts, TextInput.ts — chacun est un composant autonome avec un modèle en ligne, plus son spec et sa story. Le barrel index.ts réexporte chaque composant ; pnpm build produit un paquet au format Angular Package Format via ng-packagr.

Étape 2 — Votre premier composant

import { Component } from "@angular/core";
import { Button } from "@lilydesignsystem/angular-headless";

@Component({
  selector: "app-save-bar",
  standalone: true,
  imports: [Button],
  template: `<lily-button (click)="save()">Save</lily-button>`,
})
export class SaveBar {
  save() { console.log("saved"); }
}

Chaque composant déclare un sélecteur lily-* et rend l'élément sémantique avec son crochet de classe — <button class="button" type="button"> ici. Les entrées sont basées sur les signaux : label() pour un aria-label pour remplacer, className() pour ajouter des classes.

Étape 3 — Composer un petit formulaire

import { Component, signal } from "@angular/core";
import { Form, Field, TextInput, Button } from "@lilydesignsystem/angular-headless";

@Component({
  selector: "app-contact-form",
  standalone: true,
  imports: [Form, Field, TextInput, Button],
  template: `
    <lily-form label="Contact form" (submit)="handleSubmit($event)">
      <lily-field label="Full name" [required]="true">
        <lily-text-input id="name" label="Full name" [(value)]="name" />
      </lily-field>
      <lily-button type="submit">Save</lily-button>
    </lily-form>
  `,
})
export class ContactForm {
  name = signal("");
}

lily-field prend l'étiquette visible comme sa propre entrée label plutôt qu'un composant d'étiquette imbriqué — transmettez-la une fois, sur le champ, et le lily-text-inputcorrespondant de label fournit le nom accessible. Les composants de type saisie exposent des valeurs model() donc la liaison bidirectionnelle banana-box [(value)] fonctionne immédiatement — compatible zoneless de bout en bout.

Vérifié le 2026-08-29 : un brouillon précédent imbriquait un composant Label distinct dans lily-field, ce qui rendait deux étiquettes (une vide) parce que lily-field rend déjà la sienne à partir de l'entrée label — découvert en vérifiant le code source du composant. La route composée du formulaire de contact suit le même motif, éprouvé par de vrais tests e2e et axe.

Vérifiez votre travail : lancez l'application et le formulaire paraît nu — exactement comme prévu : le style reste à venir. Appuyez sur Tab: le focus traverse chaque contrôle, l'étiquette est annoncée et l'envoi fonctionne. Le comportement est fait ; l'apparence est la vôtre et c'est l'étape suivante.

Étape 4 — Le styler

Stylez les crochets de classe — .button, .text-input, .label, .field — dans styles.css, ou ajoutez un thème prêt à l'emploi au tableau styles dans angular.json / liez-le dans index.html:

<link rel="stylesheet" href="/themes/adobe-spectrum.css" />

Étape 5 — Voir le tout fonctionner

L'application d'exemple Angular + Analog montre le catalogue complet avec un routage par fichiers et Vite :

git clone https://github.com/LilyDesignSystem/lily-design-system
cd lily-design-system/lily-design-system-angular-examples
pnpm install && pnpm run dev

Étapes suivantes

Des questions en chemin ? La page d'aide regorge de réponses — et si une étape peut être plus claire, dites-le-nous et nous améliorerons volontiers le tutoriel.