Angular-handledning

Fristående Angular 20-komponenter — signal-indata, OnPush-ändringsdetektering, inbäddade mallar, noll NgModules, noll CSS.

Det här bygger du

Ett litet kontaktformulär — fält med etikett, skicka-knapp, tvåvägsbindning, verkligt tangentbords- och skärmläsarbeteende — först naket, sedan formgivet, på ungefär 10 minuter.

Innan du börjar

Du behöver git, Node 22 eller senare och pnpm 10 eller senare. Alla Angular 20-projekt fungerar — Angular CLI eller Analog.

Steg 1 — Hämta koden

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

Komponenterna är platta filer i components/ — Button.ts, TextInput.ts — var och en är en fristående komponent med inbäddad mall, plus sin spec och sin story. Barrel-filen index.ts återexporterar varje komponent; pnpm build skapar ett paket i Angular Package Format via ng-packagr.

Steg 2 — Din första komponent

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"); }
}

Varje komponent deklarerar en lily-* -selektor och renderar det semantiska elementet med sin klasskrok — <button class="button" type="button"> här. Indata är signalbaserade: label() för en aria-label för att åsidosätta, className() för att lägga till klasser.

Steg 3 — Sätt ihop ett litet formulär

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 tar den synliga etiketten som sin egen label -indata i stället för en nästlad etikettkomponent — skicka den en gång, på fältet, så ger den matchande lily-text-inputfrån label det tillgängliga namnet. Indataliknande komponenter exponerar model() -värden, så banana-box [(value)] -tvåvägsbindning fungerar direkt — zoneless-kompatibelt rakt igenom.

Verifierat 2026-08-29: ett tidigare utkast nästlade en separat Label -komponent i lily-field, vilket renderar två etiketter (en tom) eftersom lily-field redan renderar sin egen från label -indata — upptäckt genom att granska komponentens källkod. Den sammansatta kontaktformulärsrutten är samma mönster, prövat med riktiga e2e- och axe-tester.

Kontrollera ditt arbete: kör appen så ser formuläret kargt ut — precis som tänkt: formgivningen återstår. Tryck på Tab: fokus flyttas genom varje kontroll, etiketten läses upp och inskickning fungerar. Beteendet är klart; utseendet är ditt och kommer härnäst.

Steg 4 — Formge det

Formge klasskrokarna — .button, .text-input, .label, .field — i styles.css, eller lägg till ett färdigt tema i styles -arrayen i angular.json / länka det i index.html:

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

Steg 5 — Se allt fungera

Exempelappen Angular + Analog visar hela katalogen med filbaserad routing och Vite:

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

Nästa steg

Frågor på vägen? Hjälpsidan är full av svar — och om ett steg kan bli tydligare, berätta för oss så förbättrar vi gärna handledningen.