diff --git a/skills/angular-best-practices/README.md b/skills/angular-best-practices/README.md
new file mode 100644
index 00000000..143a521f
--- /dev/null
+++ b/skills/angular-best-practices/README.md
@@ -0,0 +1,58 @@
+# Angular Best Practices
+
+Performance optimization and best practices for Angular applications optimized for AI agents and LLMs.
+
+## Overview
+
+This skill provides prioritized performance guidelines across:
+
+- **Change Detection** - OnPush strategy, Signals, Zoneless apps
+- **Async Operations** - Avoiding waterfalls, SSR preloading
+- **Bundle Optimization** - Lazy loading, `@defer`, tree-shaking
+- **Rendering Performance** - TrackBy, virtual scrolling, CDK
+- **SSR & Hydration** - Server-side rendering patterns
+- **Template Optimization** - Structural directives, pipe memoization
+- **State Management** - Efficient reactivity patterns
+- **Memory Management** - Subscription cleanup, detached refs
+
+## Structure
+
+The `SKILL.md` file is organized by priority:
+
+1. **Critical Priority** - Largest performance gains (change detection, async)
+2. **High Priority** - Significant impact (bundles, rendering)
+3. **Medium Priority** - Noticeable improvements (SSR, templates)
+4. **Low Priority** - Incremental gains (memory, cleanup)
+
+Each rule includes:
+
+- ❌ **WRONG** - What not to do
+- ✅ **CORRECT** - Recommended pattern
+- 📝 **Why** - Explanation of the impact
+
+## Quick Reference Checklist
+
+**For New Components:**
+
+- [ ] Using `ChangeDetectionStrategy.OnPush`
+- [ ] Using Signals for reactive state
+- [ ] Using `@defer` for non-critical content
+- [ ] Using `trackBy` for `*ngFor` loops
+- [ ] No subscriptions without cleanup
+
+**For Performance Reviews:**
+
+- [ ] No async waterfalls (parallel data fetching)
+- [ ] Routes lazy-loaded
+- [ ] Large libraries code-split
+- [ ] Images use `NgOptimizedImage`
+
+## Version
+
+Current version: 1.0.0 (February 2026)
+
+## References
+
+- [Angular Performance](https://angular.dev/guide/performance)
+- [Zoneless Angular](https://angular.dev/guide/zoneless)
+- [Angular SSR](https://angular.dev/guide/ssr)
diff --git a/skills/angular-best-practices/SKILL.md b/skills/angular-best-practices/SKILL.md
new file mode 100644
index 00000000..c2920a58
--- /dev/null
+++ b/skills/angular-best-practices/SKILL.md
@@ -0,0 +1,559 @@
+---
+name: angular-best-practices
+description: Angular performance optimization and best practices guide. Use when writing, reviewing, or refactoring Angular code for optimal performance, bundle size, and rendering efficiency.
+risk: safe
+source: self
+---
+
+# Angular Best Practices
+
+Comprehensive performance optimization guide for Angular applications. Contains prioritized rules for eliminating performance bottlenecks, optimizing bundles, and improving rendering.
+
+## When to Apply
+
+Reference these guidelines when:
+
+- Writing new Angular components or pages
+- Implementing data fetching patterns
+- Reviewing code for performance issues
+- Refactoring existing Angular code
+- Optimizing bundle size or load times
+- Configuring SSR/hydration
+
+---
+
+## Rule Categories by Priority
+
+| Priority | Category | Impact | Focus |
+| -------- | --------------------- | ---------- | ------------------------------- |
+| 1 | Change Detection | CRITICAL | Signals, OnPush, Zoneless |
+| 2 | Async Waterfalls | CRITICAL | RxJS patterns, SSR preloading |
+| 3 | Bundle Optimization | CRITICAL | Lazy loading, tree shaking |
+| 4 | Rendering Performance | HIGH | @defer, trackBy, virtualization |
+| 5 | Server-Side Rendering | HIGH | Hydration, prerendering |
+| 6 | Template Optimization | MEDIUM | Control flow, pipes |
+| 7 | State Management | MEDIUM | Signal patterns, selectors |
+| 8 | Memory Management | LOW-MEDIUM | Cleanup, subscriptions |
+
+---
+
+## 1. Change Detection (CRITICAL)
+
+### Use OnPush Change Detection
+
+```typescript
+// CORRECT - OnPush with Signals
+@Component({
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: `
{{ count() }}
`,
+})
+export class CounterComponent {
+ count = signal(0);
+}
+
+// WRONG - Default change detection
+@Component({
+ template: `{{ count }}
`, // Checked every cycle
+})
+export class CounterComponent {
+ count = 0;
+}
+```
+
+### Prefer Signals Over Mutable Properties
+
+```typescript
+// CORRECT - Signals trigger precise updates
+@Component({
+ template: `
+ {{ title() }}
+ Count: {{ count() }}
+ `,
+})
+export class DashboardComponent {
+ title = signal("Dashboard");
+ count = signal(0);
+}
+
+// WRONG - Mutable properties require zone.js checks
+@Component({
+ template: `
+ {{ title }}
+ Count: {{ count }}
+ `,
+})
+export class DashboardComponent {
+ title = "Dashboard";
+ count = 0;
+}
+```
+
+### Enable Zoneless for New Projects
+
+```typescript
+// main.ts - Zoneless Angular (v20+)
+bootstrapApplication(AppComponent, {
+ providers: [provideZonelessChangeDetection()],
+});
+```
+
+**Benefits:**
+
+- No zone.js patches on async APIs
+- Smaller bundle (~15KB savings)
+- Clean stack traces for debugging
+- Better micro-frontend compatibility
+
+---
+
+## 2. Async Operations & Waterfalls (CRITICAL)
+
+### Eliminate Sequential Data Fetching
+
+```typescript
+// WRONG - Nested subscriptions create waterfalls
+this.route.params.subscribe((params) => {
+ // 1. Wait for params
+ this.userService.getUser(params.id).subscribe((user) => {
+ // 2. Wait for user
+ this.postsService.getPosts(user.id).subscribe((posts) => {
+ // 3. Wait for posts
+ });
+ });
+});
+
+// CORRECT - Parallel execution with forkJoin
+forkJoin({
+ user: this.userService.getUser(id),
+ posts: this.postsService.getPosts(id),
+}).subscribe((data) => {
+ // Fetched in parallel
+});
+
+// CORRECT - Flatten dependent calls with switchMap
+this.route.params
+ .pipe(
+ map((p) => p.id),
+ switchMap((id) => this.userService.getUser(id)),
+ )
+ .subscribe();
+```
+
+### Avoid Client-Side Waterfalls in SSR
+
+```typescript
+// CORRECT - Use resolvers or blocking hydration for critical data
+export const route: Route = {
+ path: "profile/:id",
+ resolve: { data: profileResolver }, // Fetched on server before navigation
+ component: ProfileComponent,
+};
+
+// WRONG - Component fetches data on init
+class ProfileComponent implements OnInit {
+ ngOnInit() {
+ // Starts ONLY after JS loads and component renders
+ this.http.get("/api/profile").subscribe();
+ }
+}
+```
+
+---
+
+## 3. Bundle Optimization (CRITICAL)
+
+### Lazy Load Routes
+
+```typescript
+// CORRECT - Lazy load feature routes
+export const routes: Routes = [
+ {
+ path: "admin",
+ loadChildren: () =>
+ import("./admin/admin.routes").then((m) => m.ADMIN_ROUTES),
+ },
+ {
+ path: "dashboard",
+ loadComponent: () =>
+ import("./dashboard/dashboard.component").then(
+ (m) => m.DashboardComponent,
+ ),
+ },
+];
+
+// WRONG - Eager loading everything
+import { AdminModule } from "./admin/admin.module";
+export const routes: Routes = [
+ { path: "admin", component: AdminComponent }, // In main bundle
+];
+```
+
+### Use @defer for Heavy Components
+
+```html
+
+@defer (on viewport) {
+
+} @placeholder {
+
+}
+
+
+
+```
+
+### Avoid Barrel File Re-exports
+
+```typescript
+// WRONG - Imports entire barrel, breaks tree-shaking
+import { Button, Modal, Table } from "@shared/components";
+
+// CORRECT - Direct imports
+import { Button } from "@shared/components/button/button.component";
+import { Modal } from "@shared/components/modal/modal.component";
+```
+
+### Dynamic Import Third-Party Libraries
+
+```typescript
+// CORRECT - Load heavy library on demand
+async loadChart() {
+ const { Chart } = await import('chart.js');
+ this.chart = new Chart(this.canvas, config);
+}
+
+// WRONG - Bundle Chart.js in main chunk
+import { Chart } from 'chart.js';
+```
+
+---
+
+## 4. Rendering Performance (HIGH)
+
+### Always Use trackBy with @for
+
+```html
+
+@for (item of items(); track item.id) {
+
+}
+
+
+@for (item of items(); track $index) {
+
+}
+```
+
+### Use Virtual Scrolling for Large Lists
+
+```typescript
+import { CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll } from '@angular/cdk/scrolling';
+
+@Component({
+ imports: [CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll],
+ template: `
+
+
+ {{ item.name }}
+
+
+ `
+})
+```
+
+### Prefer Pure Pipes Over Methods
+
+```typescript
+// CORRECT - Pure pipe, memoized
+@Pipe({ name: 'filterActive', standalone: true, pure: true })
+export class FilterActivePipe implements PipeTransform {
+ transform(items: Item[]): Item[] {
+ return items.filter(i => i.active);
+ }
+}
+
+// Template
+@for (item of items() | filterActive; track item.id) { ... }
+
+// WRONG - Method called every change detection
+@for (item of getActiveItems(); track item.id) { ... }
+```
+
+### Use computed() for Derived Data
+
+```typescript
+// CORRECT - Computed, cached until dependencies change
+export class ProductStore {
+ products = signal([]);
+ filter = signal('');
+
+ filteredProducts = computed(() => {
+ const f = this.filter().toLowerCase();
+ return this.products().filter(p =>
+ p.name.toLowerCase().includes(f)
+ );
+ });
+}
+
+// WRONG - Recalculates every access
+get filteredProducts() {
+ return this.products.filter(p =>
+ p.name.toLowerCase().includes(this.filter)
+ );
+}
+```
+
+---
+
+## 5. Server-Side Rendering (HIGH)
+
+### Configure Incremental Hydration
+
+```typescript
+// app.config.ts
+import {
+ provideClientHydration,
+ withIncrementalHydration,
+} from "@angular/platform-browser";
+
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideClientHydration(withIncrementalHydration(), withEventReplay()),
+ ],
+};
+```
+
+### Defer Non-Critical Content
+
+```html
+
+
+
+
+
+@defer (hydrate on viewport) {
+
+} @defer (hydrate on interaction) {
+
+}
+```
+
+### Use TransferState for SSR Data
+
+```typescript
+@Injectable({ providedIn: "root" })
+export class DataService {
+ private http = inject(HttpClient);
+ private transferState = inject(TransferState);
+ private platformId = inject(PLATFORM_ID);
+
+ getData(key: string): Observable {
+ const stateKey = makeStateKey(key);
+
+ if (isPlatformBrowser(this.platformId)) {
+ const cached = this.transferState.get(stateKey, null);
+ if (cached) {
+ this.transferState.remove(stateKey);
+ return of(cached);
+ }
+ }
+
+ return this.http.get(`/api/${key}`).pipe(
+ tap((data) => {
+ if (isPlatformServer(this.platformId)) {
+ this.transferState.set(stateKey, data);
+ }
+ }),
+ );
+ }
+}
+```
+
+---
+
+## 6. Template Optimization (MEDIUM)
+
+### Use New Control Flow Syntax
+
+```html
+
+@if (user()) {
+{{ user()!.name }}
+} @else {
+Guest
+} @for (item of items(); track item.id) {
+
+} @empty {
+No items
+}
+
+
+{{ user.name }}
+Guest
+```
+
+### Avoid Complex Template Expressions
+
+```typescript
+// CORRECT - Precompute in component
+class Component {
+ items = signal- ([]);
+ sortedItems = computed(() =>
+ [...this.items()].sort((a, b) => a.name.localeCompare(b.name))
+ );
+}
+
+// Template
+@for (item of sortedItems(); track item.id) { ... }
+
+// WRONG - Sorting in template every render
+@for (item of items() | sort:'name'; track item.id) { ... }
+```
+
+---
+
+## 7. State Management (MEDIUM)
+
+### Use Selectors to Prevent Re-renders
+
+```typescript
+// CORRECT - Selective subscription
+@Component({
+ template: `{{ userName() }}`,
+})
+class HeaderComponent {
+ private store = inject(Store);
+ // Only re-renders when userName changes
+ userName = this.store.selectSignal(selectUserName);
+}
+
+// WRONG - Subscribing to entire state
+@Component({
+ template: `{{ state().user.name }}`,
+})
+class HeaderComponent {
+ private store = inject(Store);
+ // Re-renders on ANY state change
+ state = toSignal(this.store);
+}
+```
+
+### Colocate State with Features
+
+```typescript
+// CORRECT - Feature-scoped store
+@Injectable() // NOT providedIn: 'root'
+export class ProductStore { ... }
+
+@Component({
+ providers: [ProductStore], // Scoped to component tree
+})
+export class ProductPageComponent {
+ store = inject(ProductStore);
+}
+
+// WRONG - Everything in global store
+@Injectable({ providedIn: 'root' })
+export class GlobalStore {
+ // Contains ALL app state - hard to tree-shake
+}
+```
+
+---
+
+## 8. Memory Management (LOW-MEDIUM)
+
+### Use takeUntilDestroyed for Subscriptions
+
+```typescript
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+
+@Component({...})
+export class DataComponent {
+ private destroyRef = inject(DestroyRef);
+
+ constructor() {
+ this.data$.pipe(
+ takeUntilDestroyed(this.destroyRef)
+ ).subscribe(data => this.process(data));
+ }
+}
+
+// WRONG - Manual subscription management
+export class DataComponent implements OnDestroy {
+ private subscription!: Subscription;
+
+ ngOnInit() {
+ this.subscription = this.data$.subscribe(...);
+ }
+
+ ngOnDestroy() {
+ this.subscription.unsubscribe(); // Easy to forget
+ }
+}
+```
+
+### Prefer Signals Over Subscriptions
+
+```typescript
+// CORRECT - No subscription needed
+@Component({
+ template: `
{{ data().name }}
`,
+})
+export class Component {
+ data = toSignal(this.service.data$, { initialValue: null });
+}
+
+// WRONG - Manual subscription
+@Component({
+ template: `{{ data?.name }}
`,
+})
+export class Component implements OnInit, OnDestroy {
+ data: Data | null = null;
+ private sub!: Subscription;
+
+ ngOnInit() {
+ this.sub = this.service.data$.subscribe((d) => (this.data = d));
+ }
+
+ ngOnDestroy() {
+ this.sub.unsubscribe();
+ }
+}
+```
+
+---
+
+## Quick Reference Checklist
+
+### New Component
+
+- [ ] `changeDetection: ChangeDetectionStrategy.OnPush`
+- [ ] `standalone: true`
+- [ ] Signals for state (`signal()`, `input()`, `output()`)
+- [ ] `inject()` for dependencies
+- [ ] `@for` with `track` expression
+
+### Performance Review
+
+- [ ] No methods in templates (use pipes or computed)
+- [ ] Large lists virtualized
+- [ ] Heavy components deferred
+- [ ] Routes lazy-loaded
+- [ ] Third-party libs dynamically imported
+
+### SSR Check
+
+- [ ] Hydration configured
+- [ ] Critical content renders first
+- [ ] Non-critical content uses `@defer (hydrate on ...)`
+- [ ] TransferState for server-fetched data
+
+---
+
+## Resources
+
+- [Angular Performance Guide](https://angular.dev/best-practices/performance)
+- [Zoneless Angular](https://angular.dev/guide/experimental/zoneless)
+- [Angular SSR Guide](https://angular.dev/guide/ssr)
+- [Change Detection Deep Dive](https://angular.dev/guide/change-detection)
diff --git a/skills/angular-best-practices/metadata.json b/skills/angular-best-practices/metadata.json
new file mode 100644
index 00000000..633f57c6
--- /dev/null
+++ b/skills/angular-best-practices/metadata.json
@@ -0,0 +1,13 @@
+{
+ "version": "1.0.0",
+ "organization": "Antigravity Awesome Skills",
+ "date": "February 2026",
+ "abstract": "Performance optimization and best practices guide for Angular applications designed for AI agents and LLMs. Covers change detection strategies (OnPush, Signals, Zoneless), avoiding async waterfalls, bundle optimization with lazy loading and @defer, rendering performance, SSR/hydration patterns, and memory management. Prioritized by impact from critical to incremental improvements.",
+ "references": [
+ "https://angular.dev/best-practices",
+ "https://angular.dev/guide/performance",
+ "https://angular.dev/guide/zoneless",
+ "https://angular.dev/guide/ssr",
+ "https://web.dev/performance"
+ ]
+}
diff --git a/skills/angular-state-management/README.md b/skills/angular-state-management/README.md
new file mode 100644
index 00000000..e8ffb15e
--- /dev/null
+++ b/skills/angular-state-management/README.md
@@ -0,0 +1,41 @@
+# Angular State Management
+
+Complete state management patterns for Angular applications optimized for AI agents and LLMs.
+
+## Overview
+
+This skill provides decision frameworks and implementation patterns for:
+
+- **Signal-based Services** - Lightweight state for shared data
+- **NgRx SignalStore** - Feature-scoped state with computed values
+- **NgRx Store** - Enterprise-scale global state management
+- **RxJS ComponentStore** - Reactive component-level state
+- **Forms State** - Reactive and template-driven form patterns
+
+## Structure
+
+The `SKILL.md` file is organized into:
+
+1. **State Categories** - Local, shared, global, server, URL, and form state
+2. **Selection Criteria** - Decision trees for choosing the right solution
+3. **Implementation Patterns** - Complete examples for each approach
+4. **Migration Guides** - Moving from BehaviorSubject to Signals
+5. **Bridging Patterns** - Integrating Signals with RxJS
+
+## When to Use Each Pattern
+
+- **Signal Service**: Shared UI state (theme, user preferences)
+- **NgRx SignalStore**: Feature state with computed values
+- **NgRx Store**: Complex cross-feature dependencies
+- **ComponentStore**: Component-scoped async operations
+- **Reactive Forms**: Form state with validation
+
+## Version
+
+Current version: 1.0.0 (February 2026)
+
+## References
+
+- [Angular Signals](https://angular.dev/guide/signals)
+- [NgRx](https://ngrx.io)
+- [NgRx SignalStore](https://ngrx.io/guide/signals)
diff --git a/skills/angular-state-management/SKILL.md b/skills/angular-state-management/SKILL.md
new file mode 100644
index 00000000..c57b1885
--- /dev/null
+++ b/skills/angular-state-management/SKILL.md
@@ -0,0 +1,634 @@
+---
+name: angular-state-management
+description: Master modern Angular state management with Signals, NgRx, and RxJS. Use when setting up global state, managing component stores, choosing between state solutions, or migrating from legacy patterns.
+risk: safe
+source: self
+---
+
+# Angular State Management
+
+Comprehensive guide to modern Angular state management patterns, from Signal-based local state to global stores and server state synchronization.
+
+## When to Use This Skill
+
+- Setting up global state management in Angular
+- Choosing between Signals, NgRx, or Akita
+- Managing component-level stores
+- Implementing optimistic updates
+- Debugging state-related issues
+- Migrating from legacy state patterns
+
+## Do Not Use This Skill When
+
+- The task is unrelated to Angular state management
+- You need React state management → use `react-state-management`
+
+---
+
+## Core Concepts
+
+### State Categories
+
+| Type | Description | Solutions |
+| ---------------- | ---------------------------- | --------------------- |
+| **Local State** | Component-specific, UI state | Signals, `signal()` |
+| **Shared State** | Between related components | Signal services |
+| **Global State** | App-wide, complex | NgRx, Akita, Elf |
+| **Server State** | Remote data, caching | NgRx Query, RxAngular |
+| **URL State** | Route parameters | ActivatedRoute |
+| **Form State** | Input values, validation | Reactive Forms |
+
+### Selection Criteria
+
+```
+Small app, simple state → Signal Services
+Medium app, moderate state → Component Stores
+Large app, complex state → NgRx Store
+Heavy server interaction → NgRx Query + Signal Services
+Real-time updates → RxAngular + Signals
+```
+
+---
+
+## Quick Start: Signal-Based State
+
+### Pattern 1: Simple Signal Service
+
+```typescript
+// services/counter.service.ts
+import { Injectable, signal, computed } from "@angular/core";
+
+@Injectable({ providedIn: "root" })
+export class CounterService {
+ // Private writable signals
+ private _count = signal(0);
+
+ // Public read-only
+ readonly count = this._count.asReadonly();
+ readonly doubled = computed(() => this._count() * 2);
+ readonly isPositive = computed(() => this._count() > 0);
+
+ increment() {
+ this._count.update((v) => v + 1);
+ }
+
+ decrement() {
+ this._count.update((v) => v - 1);
+ }
+
+ reset() {
+ this._count.set(0);
+ }
+}
+
+// Usage in component
+@Component({
+ template: `
+ Count: {{ counter.count() }}
+ Doubled: {{ counter.doubled() }}
+
+ `,
+})
+export class CounterComponent {
+ counter = inject(CounterService);
+}
+```
+
+### Pattern 2: Feature Signal Store
+
+```typescript
+// stores/user.store.ts
+import { Injectable, signal, computed, inject } from "@angular/core";
+import { HttpClient } from "@angular/common/http";
+import { toSignal } from "@angular/core/rxjs-interop";
+
+interface User {
+ id: string;
+ name: string;
+ email: string;
+}
+
+interface UserState {
+ user: User | null;
+ loading: boolean;
+ error: string | null;
+}
+
+@Injectable({ providedIn: "root" })
+export class UserStore {
+ private http = inject(HttpClient);
+
+ // State signals
+ private _user = signal(null);
+ private _loading = signal(false);
+ private _error = signal(null);
+
+ // Selectors (read-only computed)
+ readonly user = computed(() => this._user());
+ readonly loading = computed(() => this._loading());
+ readonly error = computed(() => this._error());
+ readonly isAuthenticated = computed(() => this._user() !== null);
+ readonly displayName = computed(() => this._user()?.name ?? "Guest");
+
+ // Actions
+ async loadUser(id: string) {
+ this._loading.set(true);
+ this._error.set(null);
+
+ try {
+ const user = await fetch(`/api/users/${id}`).then((r) => r.json());
+ this._user.set(user);
+ } catch (e) {
+ this._error.set("Failed to load user");
+ } finally {
+ this._loading.set(false);
+ }
+ }
+
+ updateUser(updates: Partial) {
+ this._user.update((user) => (user ? { ...user, ...updates } : null));
+ }
+
+ logout() {
+ this._user.set(null);
+ this._error.set(null);
+ }
+}
+```
+
+### Pattern 3: SignalStore (NgRx Signals)
+
+```typescript
+// stores/products.store.ts
+import {
+ signalStore,
+ withState,
+ withMethods,
+ withComputed,
+ patchState,
+} from "@ngrx/signals";
+import { inject } from "@angular/core";
+import { ProductService } from "./product.service";
+
+interface ProductState {
+ products: Product[];
+ loading: boolean;
+ filter: string;
+}
+
+const initialState: ProductState = {
+ products: [],
+ loading: false,
+ filter: "",
+};
+
+export const ProductStore = signalStore(
+ { providedIn: "root" },
+
+ withState(initialState),
+
+ withComputed((store) => ({
+ filteredProducts: computed(() => {
+ const filter = store.filter().toLowerCase();
+ return store
+ .products()
+ .filter((p) => p.name.toLowerCase().includes(filter));
+ }),
+ totalCount: computed(() => store.products().length),
+ })),
+
+ withMethods((store, productService = inject(ProductService)) => ({
+ async loadProducts() {
+ patchState(store, { loading: true });
+
+ try {
+ const products = await productService.getAll();
+ patchState(store, { products, loading: false });
+ } catch {
+ patchState(store, { loading: false });
+ }
+ },
+
+ setFilter(filter: string) {
+ patchState(store, { filter });
+ },
+
+ addProduct(product: Product) {
+ patchState(store, ({ products }) => ({
+ products: [...products, product],
+ }));
+ },
+ })),
+);
+
+// Usage
+@Component({
+ template: `
+
+ @if (store.loading()) {
+
+ } @else {
+ @for (product of store.filteredProducts(); track product.id) {
+
+ }
+ }
+ `,
+})
+export class ProductListComponent {
+ store = inject(ProductStore);
+
+ ngOnInit() {
+ this.store.loadProducts();
+ }
+}
+```
+
+---
+
+## NgRx Store (Global State)
+
+### Setup
+
+```typescript
+// store/app.state.ts
+import { ActionReducerMap } from "@ngrx/store";
+
+export interface AppState {
+ user: UserState;
+ cart: CartState;
+}
+
+export const reducers: ActionReducerMap = {
+ user: userReducer,
+ cart: cartReducer,
+};
+
+// main.ts
+bootstrapApplication(AppComponent, {
+ providers: [
+ provideStore(reducers),
+ provideEffects([UserEffects, CartEffects]),
+ provideStoreDevtools({ maxAge: 25 }),
+ ],
+});
+```
+
+### Feature Slice Pattern
+
+```typescript
+// store/user/user.actions.ts
+import { createActionGroup, props, emptyProps } from "@ngrx/store";
+
+export const UserActions = createActionGroup({
+ source: "User",
+ events: {
+ "Load User": props<{ userId: string }>(),
+ "Load User Success": props<{ user: User }>(),
+ "Load User Failure": props<{ error: string }>(),
+ "Update User": props<{ updates: Partial }>(),
+ Logout: emptyProps(),
+ },
+});
+```
+
+```typescript
+// store/user/user.reducer.ts
+import { createReducer, on } from "@ngrx/store";
+import { UserActions } from "./user.actions";
+
+export interface UserState {
+ user: User | null;
+ loading: boolean;
+ error: string | null;
+}
+
+const initialState: UserState = {
+ user: null,
+ loading: false,
+ error: null,
+};
+
+export const userReducer = createReducer(
+ initialState,
+
+ on(UserActions.loadUser, (state) => ({
+ ...state,
+ loading: true,
+ error: null,
+ })),
+
+ on(UserActions.loadUserSuccess, (state, { user }) => ({
+ ...state,
+ user,
+ loading: false,
+ })),
+
+ on(UserActions.loadUserFailure, (state, { error }) => ({
+ ...state,
+ loading: false,
+ error,
+ })),
+
+ on(UserActions.logout, () => initialState),
+);
+```
+
+```typescript
+// store/user/user.selectors.ts
+import { createFeatureSelector, createSelector } from "@ngrx/store";
+import { UserState } from "./user.reducer";
+
+export const selectUserState = createFeatureSelector("user");
+
+export const selectUser = createSelector(
+ selectUserState,
+ (state) => state.user,
+);
+
+export const selectUserLoading = createSelector(
+ selectUserState,
+ (state) => state.loading,
+);
+
+export const selectIsAuthenticated = createSelector(
+ selectUser,
+ (user) => user !== null,
+);
+```
+
+```typescript
+// store/user/user.effects.ts
+import { Injectable, inject } from "@angular/core";
+import { Actions, createEffect, ofType } from "@ngrx/effects";
+import { switchMap, map, catchError, of } from "rxjs";
+
+@Injectable()
+export class UserEffects {
+ private actions$ = inject(Actions);
+ private userService = inject(UserService);
+
+ loadUser$ = createEffect(() =>
+ this.actions$.pipe(
+ ofType(UserActions.loadUser),
+ switchMap(({ userId }) =>
+ this.userService.getUser(userId).pipe(
+ map((user) => UserActions.loadUserSuccess({ user })),
+ catchError((error) =>
+ of(UserActions.loadUserFailure({ error: error.message })),
+ ),
+ ),
+ ),
+ ),
+ );
+}
+```
+
+### Component Usage
+
+```typescript
+@Component({
+ template: `
+ @if (loading()) {
+
+ } @else if (user(); as user) {
+ Welcome, {{ user.name }}
+
+ }
+ `,
+})
+export class HeaderComponent {
+ private store = inject(Store);
+
+ user = this.store.selectSignal(selectUser);
+ loading = this.store.selectSignal(selectUserLoading);
+
+ logout() {
+ this.store.dispatch(UserActions.logout());
+ }
+}
+```
+
+---
+
+## RxJS-Based Patterns
+
+### Component Store (Local Feature State)
+
+```typescript
+// stores/todo.store.ts
+import { Injectable } from "@angular/core";
+import { ComponentStore } from "@ngrx/component-store";
+import { switchMap, tap, catchError, EMPTY } from "rxjs";
+
+interface TodoState {
+ todos: Todo[];
+ loading: boolean;
+}
+
+@Injectable()
+export class TodoStore extends ComponentStore {
+ constructor(private todoService: TodoService) {
+ super({ todos: [], loading: false });
+ }
+
+ // Selectors
+ readonly todos$ = this.select((state) => state.todos);
+ readonly loading$ = this.select((state) => state.loading);
+ readonly completedCount$ = this.select(
+ this.todos$,
+ (todos) => todos.filter((t) => t.completed).length,
+ );
+
+ // Updaters
+ readonly addTodo = this.updater((state, todo: Todo) => ({
+ ...state,
+ todos: [...state.todos, todo],
+ }));
+
+ readonly toggleTodo = this.updater((state, id: string) => ({
+ ...state,
+ todos: state.todos.map((t) =>
+ t.id === id ? { ...t, completed: !t.completed } : t,
+ ),
+ }));
+
+ // Effects
+ readonly loadTodos = this.effect((trigger$) =>
+ trigger$.pipe(
+ tap(() => this.patchState({ loading: true })),
+ switchMap(() =>
+ this.todoService.getAll().pipe(
+ tap({
+ next: (todos) => this.patchState({ todos, loading: false }),
+ error: () => this.patchState({ loading: false }),
+ }),
+ catchError(() => EMPTY),
+ ),
+ ),
+ ),
+ );
+}
+```
+
+---
+
+## Server State with Signals
+
+### HTTP + Signals Pattern
+
+```typescript
+// services/api.service.ts
+import { Injectable, signal, inject } from "@angular/core";
+import { HttpClient } from "@angular/common/http";
+import { toSignal } from "@angular/core/rxjs-interop";
+
+interface ApiState {
+ data: T | null;
+ loading: boolean;
+ error: string | null;
+}
+
+@Injectable({ providedIn: "root" })
+export class ProductApiService {
+ private http = inject(HttpClient);
+
+ private _state = signal>({
+ data: null,
+ loading: false,
+ error: null,
+ });
+
+ readonly products = computed(() => this._state().data ?? []);
+ readonly loading = computed(() => this._state().loading);
+ readonly error = computed(() => this._state().error);
+
+ async fetchProducts(): Promise {
+ this._state.update((s) => ({ ...s, loading: true, error: null }));
+
+ try {
+ const data = await firstValueFrom(
+ this.http.get("/api/products"),
+ );
+ this._state.update((s) => ({ ...s, data, loading: false }));
+ } catch (e) {
+ this._state.update((s) => ({
+ ...s,
+ loading: false,
+ error: "Failed to fetch products",
+ }));
+ }
+ }
+
+ // Optimistic update
+ async deleteProduct(id: string): Promise {
+ const previousData = this._state().data;
+
+ // Optimistically remove
+ this._state.update((s) => ({
+ ...s,
+ data: s.data?.filter((p) => p.id !== id) ?? null,
+ }));
+
+ try {
+ await firstValueFrom(this.http.delete(`/api/products/${id}`));
+ } catch {
+ // Rollback on error
+ this._state.update((s) => ({ ...s, data: previousData }));
+ }
+ }
+}
+```
+
+---
+
+## Best Practices
+
+### Do's
+
+| Practice | Why |
+| ---------------------------------- | ---------------------------------- |
+| Use Signals for local state | Simple, reactive, no subscriptions |
+| Use `computed()` for derived data | Auto-updates, memoized |
+| Colocate state with feature | Easier to maintain |
+| Use NgRx for complex flows | Actions, effects, devtools |
+| Prefer `inject()` over constructor | Cleaner, works in factories |
+
+### Don'ts
+
+| Anti-Pattern | Instead |
+| --------------------------------- | ----------------------------------------------------- |
+| Store derived data | Use `computed()` |
+| Mutate signals directly | Use `set()` or `update()` |
+| Over-globalize state | Keep local when possible |
+| Mix RxJS and Signals chaotically | Choose primary, bridge with `toSignal`/`toObservable` |
+| Subscribe in components for state | Use template with signals |
+
+---
+
+## Migration Path
+
+### From BehaviorSubject to Signals
+
+```typescript
+// Before: RxJS-based
+@Injectable({ providedIn: "root" })
+export class OldUserService {
+ private userSubject = new BehaviorSubject(null);
+ user$ = this.userSubject.asObservable();
+
+ setUser(user: User) {
+ this.userSubject.next(user);
+ }
+}
+
+// After: Signal-based
+@Injectable({ providedIn: "root" })
+export class UserService {
+ private _user = signal(null);
+ readonly user = this._user.asReadonly();
+
+ setUser(user: User) {
+ this._user.set(user);
+ }
+}
+```
+
+### Bridging Signals and RxJS
+
+```typescript
+import { toSignal, toObservable } from '@angular/core/rxjs-interop';
+
+// Observable → Signal
+@Component({...})
+export class ExampleComponent {
+ private route = inject(ActivatedRoute);
+
+ // Convert Observable to Signal
+ userId = toSignal(
+ this.route.params.pipe(map(p => p['id'])),
+ { initialValue: '' }
+ );
+}
+
+// Signal → Observable
+export class DataService {
+ private filter = signal('');
+
+ // Convert Signal to Observable
+ filter$ = toObservable(this.filter);
+
+ filteredData$ = this.filter$.pipe(
+ debounceTime(300),
+ switchMap(filter => this.http.get(`/api/data?q=${filter}`))
+ );
+}
+```
+
+---
+
+## Resources
+
+- [Angular Signals Guide](https://angular.dev/guide/signals)
+- [NgRx Documentation](https://ngrx.io/)
+- [NgRx SignalStore](https://ngrx.io/guide/signals)
+- [RxAngular](https://www.rx-angular.io/)
diff --git a/skills/angular-state-management/metadata.json b/skills/angular-state-management/metadata.json
new file mode 100644
index 00000000..97132e00
--- /dev/null
+++ b/skills/angular-state-management/metadata.json
@@ -0,0 +1,13 @@
+{
+ "version": "1.0.0",
+ "organization": "Antigravity Awesome Skills",
+ "date": "February 2026",
+ "abstract": "Complete state management guide for Angular applications designed for AI agents and LLMs. Covers Signal-based services, NgRx for global state, RxJS patterns, and component stores. Includes decision trees for choosing the right solution, migration patterns from BehaviorSubject to Signals, and strategies for bridging Signals with RxJS observables.",
+ "references": [
+ "https://angular.dev/guide/signals",
+ "https://ngrx.io",
+ "https://ngrx.io/guide/signals",
+ "https://www.rx-angular.io",
+ "https://github.com/ngrx/platform"
+ ]
+}
diff --git a/skills/angular-ui-patterns/README.md b/skills/angular-ui-patterns/README.md
new file mode 100644
index 00000000..521301c0
--- /dev/null
+++ b/skills/angular-ui-patterns/README.md
@@ -0,0 +1,55 @@
+# Angular UI Patterns
+
+Modern UI patterns for building robust Angular applications optimized for AI agents and LLMs.
+
+## Overview
+
+This skill covers essential UI patterns for:
+
+- **Loading States** - Skeleton vs spinner decision trees
+- **Error Handling** - Error boundary hierarchy and recovery
+- **Progressive Disclosure** - Using `@defer` for lazy rendering
+- **Data Display** - Handling empty, loading, and error states
+- **Form Patterns** - Submission states and validation feedback
+- **Dialog/Modal Patterns** - Proper dialog lifecycle management
+
+## Core Principles
+
+1. **Never show stale UI** - Only show loading when no data exists
+2. **Surface all errors** - Never silently fail
+3. **Optimistic updates** - Update UI before server confirms
+4. **Progressive disclosure** - Use `@defer` to load non-critical content
+5. **Graceful degradation** - Fallback for failed features
+
+## Structure
+
+The `SKILL.md` file includes:
+
+1. **Golden Rules** - Non-negotiable patterns to follow
+2. **Decision Trees** - When to use skeleton vs spinner
+3. **Code Examples** - Correct vs incorrect implementations
+4. **Anti-patterns** - Common mistakes to avoid
+
+## Quick Reference
+
+```html
+
+@if (error()) {
+
+} @else if (loading() && !data()) {
+
+} @else if (!data()?.length) {
+
+} @else {
+
+}
+```
+
+## Version
+
+Current version: 1.0.0 (February 2026)
+
+## References
+
+- [Angular @defer](https://angular.dev/guide/defer)
+- [Angular Templates](https://angular.dev/guide/templates)
diff --git a/skills/angular-ui-patterns/SKILL.md b/skills/angular-ui-patterns/SKILL.md
new file mode 100644
index 00000000..57b40126
--- /dev/null
+++ b/skills/angular-ui-patterns/SKILL.md
@@ -0,0 +1,508 @@
+---
+name: angular-ui-patterns
+description: Modern Angular UI patterns for loading states, error handling, and data display. Use when building UI components, handling async data, or managing component states.
+risk: safe
+source: self
+---
+
+# Angular UI Patterns
+
+## Core Principles
+
+1. **Never show stale UI** - Loading states only when actually loading
+2. **Always surface errors** - Users must know when something fails
+3. **Optimistic updates** - Make the UI feel instant
+4. **Progressive disclosure** - Use `@defer` to show content as available
+5. **Graceful degradation** - Partial data is better than no data
+
+---
+
+## Loading State Patterns
+
+### The Golden Rule
+
+**Show loading indicator ONLY when there's no data to display.**
+
+```typescript
+@Component({
+ template: `
+ @if (error()) {
+
+ } @else if (loading() && !items().length) {
+
+ } @else if (!items().length) {
+
+ } @else {
+
+ }
+ `,
+})
+export class ItemListComponent {
+ private store = inject(ItemStore);
+
+ items = this.store.items;
+ loading = this.store.loading;
+ error = this.store.error;
+}
+```
+
+### Loading State Decision Tree
+
+```
+Is there an error?
+ → Yes: Show error state with retry option
+ → No: Continue
+
+Is it loading AND we have no data?
+ → Yes: Show loading indicator (spinner/skeleton)
+ → No: Continue
+
+Do we have data?
+ → Yes, with items: Show the data
+ → Yes, but empty: Show empty state
+ → No: Show loading (fallback)
+```
+
+### Skeleton vs Spinner
+
+| Use Skeleton When | Use Spinner When |
+| -------------------- | --------------------- |
+| Known content shape | Unknown content shape |
+| List/card layouts | Modal actions |
+| Initial page load | Button submissions |
+| Content placeholders | Inline operations |
+
+---
+
+## Control Flow Patterns
+
+### @if/@else for Conditional Rendering
+
+```html
+@if (user(); as user) {
+Welcome, {{ user.name }}
+} @else if (loading()) {
+
+} @else {
+Sign In
+}
+```
+
+### @for with Track
+
+```html
+@for (item of items(); track item.id) {
+
+} @empty {
+
+}
+```
+
+### @defer for Progressive Loading
+
+```html
+
+
+
+
+
+@defer (on viewport) {
+
+} @placeholder {
+
+} @loading (minimum 200ms) {
+
+} @error {
+
+}
+```
+
+---
+
+## Error Handling Patterns
+
+### Error Handling Hierarchy
+
+```
+1. Inline error (field-level) → Form validation errors
+2. Toast notification → Recoverable errors, user can retry
+3. Error banner → Page-level errors, data still partially usable
+4. Full error screen → Unrecoverable, needs user action
+```
+
+### Always Show Errors
+
+**CRITICAL: Never swallow errors silently.**
+
+```typescript
+// CORRECT - Error always surfaced to user
+@Component({...})
+export class CreateItemComponent {
+ private store = inject(ItemStore);
+ private toast = inject(ToastService);
+
+ async create(data: CreateItemDto) {
+ try {
+ await this.store.create(data);
+ this.toast.success('Item created successfully');
+ this.router.navigate(['/items']);
+ } catch (error) {
+ console.error('createItem failed:', error);
+ this.toast.error('Failed to create item. Please try again.');
+ }
+ }
+}
+
+// WRONG - Error silently caught
+async create(data: CreateItemDto) {
+ try {
+ await this.store.create(data);
+ } catch (error) {
+ console.error(error); // User sees nothing!
+ }
+}
+```
+
+### Error State Component Pattern
+
+```typescript
+@Component({
+ selector: "app-error-state",
+ standalone: true,
+ imports: [NgOptimizedImage],
+ template: `
+
+
![]()
+
{{ title() }}
+
{{ message() }}
+ @if (retry.observed) {
+
+ }
+
+ `,
+})
+export class ErrorStateComponent {
+ title = input("Something went wrong");
+ message = input("An unexpected error occurred");
+ retry = output();
+}
+```
+
+---
+
+## Button State Patterns
+
+### Button Loading State
+
+```html
+
+```
+
+### Disable During Operations
+
+**CRITICAL: Always disable triggers during async operations.**
+
+```typescript
+// CORRECT - Button disabled while loading
+@Component({
+ template: `
+
+ `
+})
+export class SaveButtonComponent {
+ saving = signal(false);
+
+ async save() {
+ this.saving.set(true);
+ try {
+ await this.service.save();
+ } finally {
+ this.saving.set(false);
+ }
+ }
+}
+
+// WRONG - User can click multiple times
+
+```
+
+---
+
+## Empty States
+
+### Empty State Requirements
+
+Every list/collection MUST have an empty state:
+
+```html
+@for (item of items(); track item.id) {
+
+} @empty {
+
+}
+```
+
+### Contextual Empty States
+
+```typescript
+@Component({
+ selector: "app-empty-state",
+ template: `
+
+
+
{{ title() }}
+
{{ description() }}
+ @if (actionLabel()) {
+
+ }
+
+ `,
+})
+export class EmptyStateComponent {
+ icon = input("inbox");
+ title = input.required();
+ description = input("");
+ actionLabel = input(null);
+ action = output();
+}
+```
+
+---
+
+## Form Patterns
+
+### Form with Loading and Validation
+
+```typescript
+@Component({
+ template: `
+
+ `,
+})
+export class UserFormComponent {
+ private fb = inject(FormBuilder);
+
+ submitting = signal(false);
+
+ form = this.fb.group({
+ name: ["", [Validators.required, Validators.minLength(2)]],
+ email: ["", [Validators.required, Validators.email]],
+ });
+
+ isFieldInvalid(field: string): boolean {
+ const control = this.form.get(field);
+ return control ? control.invalid && control.touched : false;
+ }
+
+ getFieldError(field: string): string {
+ const control = this.form.get(field);
+ if (control?.hasError("required")) return "This field is required";
+ if (control?.hasError("email")) return "Invalid email format";
+ if (control?.hasError("minlength")) return "Too short";
+ return "";
+ }
+
+ async onSubmit() {
+ if (this.form.invalid) return;
+
+ this.submitting.set(true);
+ try {
+ await this.service.submit(this.form.value);
+ this.toast.success("Submitted successfully");
+ } catch {
+ this.toast.error("Submission failed");
+ } finally {
+ this.submitting.set(false);
+ }
+ }
+}
+```
+
+---
+
+## Dialog/Modal Patterns
+
+### Confirmation Dialog
+
+```typescript
+// dialog.service.ts
+@Injectable({ providedIn: 'root' })
+export class DialogService {
+ private dialog = inject(Dialog); // CDK Dialog or custom
+
+ async confirm(options: {
+ title: string;
+ message: string;
+ confirmText?: string;
+ cancelText?: string;
+ }): Promise {
+ const dialogRef = this.dialog.open(ConfirmDialogComponent, {
+ data: options,
+ });
+
+ return await firstValueFrom(dialogRef.closed) ?? false;
+ }
+}
+
+// Usage
+async deleteItem(item: Item) {
+ const confirmed = await this.dialog.confirm({
+ title: 'Delete Item',
+ message: `Are you sure you want to delete "${item.name}"?`,
+ confirmText: 'Delete',
+ });
+
+ if (confirmed) {
+ await this.store.delete(item.id);
+ }
+}
+```
+
+---
+
+## Anti-Patterns
+
+### Loading States
+
+```typescript
+// WRONG - Spinner when data exists (causes flash on refetch)
+@if (loading()) {
+
+}
+
+// CORRECT - Only show loading without data
+@if (loading() && !items().length) {
+
+}
+```
+
+### Error Handling
+
+```typescript
+// WRONG - Error swallowed
+try {
+ await this.service.save();
+} catch (e) {
+ console.log(e); // User has no idea!
+}
+
+// CORRECT - Error surfaced
+try {
+ await this.service.save();
+} catch (e) {
+ console.error("Save failed:", e);
+ this.toast.error("Failed to save. Please try again.");
+}
+```
+
+### Button States
+
+```html
+
+
+
+
+
+```
+
+---
+
+## UI State Checklist
+
+Before completing any UI component:
+
+### UI States
+
+- [ ] Error state handled and shown to user
+- [ ] Loading state shown only when no data exists
+- [ ] Empty state provided for collections (`@empty` block)
+- [ ] Buttons disabled during async operations
+- [ ] Buttons show loading indicator when appropriate
+
+### Data & Mutations
+
+- [ ] All async operations have error handling
+- [ ] All user actions have feedback (toast/visual)
+- [ ] Optimistic updates rollback on failure
+
+### Accessibility
+
+- [ ] Loading states announced to screen readers
+- [ ] Error messages linked to form fields
+- [ ] Focus management after state changes
+
+---
+
+## Integration with Other Skills
+
+- **angular-state-management**: Use Signal stores for state
+- **angular**: Apply modern patterns (Signals, @defer)
+- **testing-patterns**: Test all UI states
diff --git a/skills/angular-ui-patterns/metadata.json b/skills/angular-ui-patterns/metadata.json
new file mode 100644
index 00000000..38a0f5c9
--- /dev/null
+++ b/skills/angular-ui-patterns/metadata.json
@@ -0,0 +1,12 @@
+{
+ "version": "1.0.0",
+ "organization": "Antigravity Awesome Skills",
+ "date": "February 2026",
+ "abstract": "Modern UI patterns for Angular applications designed for AI agents and LLMs. Covers loading states, error handling, progressive disclosure, and data display patterns. Emphasizes showing loading only without data, surfacing all errors, optimistic updates, and graceful degradation using @defer. Includes decision trees and anti-patterns to avoid.",
+ "references": [
+ "https://angular.dev/guide/defer",
+ "https://angular.dev/guide/templates",
+ "https://material.angular.io",
+ "https://ng-spartan.com"
+ ]
+}
diff --git a/skills/angular/README.md b/skills/angular/README.md
new file mode 100644
index 00000000..1929725e
--- /dev/null
+++ b/skills/angular/README.md
@@ -0,0 +1,40 @@
+# Angular
+
+A comprehensive guide to modern Angular development (v20+) optimized for AI agents and LLMs.
+
+## Overview
+
+This skill covers modern Angular patterns including:
+
+- **Signals** - Angular's reactive primitive for state management
+- **Standalone Components** - Modern component architecture without NgModules
+- **Zoneless Applications** - High-performance apps without Zone.js
+- **SSR & Hydration** - Server-side rendering and client hydration patterns
+- **Modern Routing** - Functional guards, resolvers, and lazy loading
+- **Dependency Injection** - Modern DI with `inject()` function
+- **Reactive Forms** - Type-safe form handling
+
+## Structure
+
+This skill is a single, comprehensive `SKILL.md` file containing:
+
+1. Modern component patterns with Signal inputs/outputs
+2. State management with Signals and computed values
+3. Performance optimization techniques
+4. SSR and hydration best practices
+5. Migration strategies from legacy Angular patterns
+
+## Usage
+
+This skill is designed to be read in full to understand the complete modern Angular development approach, or referenced for specific patterns when needed.
+
+## Version
+
+Current version: 1.0.0 (February 2026)
+
+## References
+
+- [Angular Documentation](https://angular.dev)
+- [Angular Signals](https://angular.dev/guide/signals)
+- [Zoneless Angular](https://angular.dev/guide/zoneless)
+- [Angular SSR](https://angular.dev/guide/ssr)
diff --git a/skills/angular/SKILL.md b/skills/angular/SKILL.md
new file mode 100644
index 00000000..7352d107
--- /dev/null
+++ b/skills/angular/SKILL.md
@@ -0,0 +1,821 @@
+---
+name: angular
+description: >-
+ Modern Angular (v20+) expert with deep knowledge of Signals, Standalone
+ Components, Zoneless applications, SSR/Hydration, and reactive patterns.
+ Use PROACTIVELY for Angular development, component architecture, state
+ management, performance optimization, and migration to modern patterns.
+risk: safe
+source: self
+---
+
+# Angular Expert
+
+Master modern Angular development with Signals, Standalone Components, Zoneless applications, SSR/Hydration, and the latest reactive patterns.
+
+## When to Use This Skill
+
+- Building new Angular applications (v20+)
+- Implementing Signals-based reactive patterns
+- Creating Standalone Components and migrating from NgModules
+- Configuring Zoneless Angular applications
+- Implementing SSR, prerendering, and hydration
+- Optimizing Angular performance
+- Adopting modern Angular patterns and best practices
+
+## Do Not Use This Skill When
+
+- Migrating from AngularJS (1.x) → use `angular-migration` skill
+- Working with legacy Angular apps that cannot upgrade
+- General TypeScript issues → use `typescript-expert` skill
+
+## Instructions
+
+1. Assess the Angular version and project structure
+2. Apply modern patterns (Signals, Standalone, Zoneless)
+3. Implement with proper typing and reactivity
+4. Validate with build and tests
+
+## Safety
+
+- Always test changes in development before production
+- Gradual migration for existing apps (don't big-bang refactor)
+- Keep backward compatibility during transitions
+
+---
+
+## Angular Version Timeline
+
+| Version | Release | Key Features |
+| -------------- | ------- | ------------------------------------------------------ |
+| **Angular 20** | Q2 2025 | Signals stable, Zoneless stable, Incremental hydration |
+| **Angular 21** | Q4 2025 | Signals-first default, Enhanced SSR |
+| **Angular 22** | Q2 2026 | Signal Forms, Selectorless components |
+
+---
+
+## 1. Signals: The New Reactive Primitive
+
+Signals are Angular's fine-grained reactivity system, replacing zone.js-based change detection.
+
+### Core Concepts
+
+```typescript
+import { signal, computed, effect } from "@angular/core";
+
+// Writable signal
+const count = signal(0);
+
+// Read value
+console.log(count()); // 0
+
+// Update value
+count.set(5); // Direct set
+count.update((v) => v + 1); // Functional update
+
+// Computed (derived) signal
+const doubled = computed(() => count() * 2);
+
+// Effect (side effects)
+effect(() => {
+ console.log(`Count changed to: ${count()}`);
+});
+```
+
+### Signal-Based Inputs and Outputs
+
+```typescript
+import { Component, input, output, model } from "@angular/core";
+
+@Component({
+ selector: "app-user-card",
+ standalone: true,
+ template: `
+
+
{{ name() }}
+ {{ role() }}
+
+
+ `,
+})
+export class UserCardComponent {
+ // Signal inputs (read-only)
+ id = input.required();
+ name = input.required();
+ role = input("User"); // With default
+
+ // Output
+ select = output();
+
+ // Two-way binding (model)
+ isSelected = model(false);
+}
+
+// Usage:
+//
+```
+
+### Signal Queries (ViewChild/ContentChild)
+
+```typescript
+import {
+ Component,
+ viewChild,
+ viewChildren,
+ contentChild,
+} from "@angular/core";
+
+@Component({
+ selector: "app-container",
+ standalone: true,
+ template: `
+
+
+ `,
+})
+export class ContainerComponent {
+ // Signal-based queries
+ searchInput = viewChild("searchInput");
+ items = viewChildren(ItemComponent);
+ projectedContent = contentChild(HeaderDirective);
+
+ focusSearch() {
+ this.searchInput()?.nativeElement.focus();
+ }
+}
+```
+
+### When to Use Signals vs RxJS
+
+| Use Case | Signals | RxJS |
+| ----------------------- | --------------- | -------------------------------- |
+| Local component state | ✅ Preferred | Overkill |
+| Derived/computed values | ✅ `computed()` | `combineLatest` works |
+| Side effects | ✅ `effect()` | `tap` operator |
+| HTTP requests | ❌ | ✅ HttpClient returns Observable |
+| Event streams | ❌ | ✅ `fromEvent`, operators |
+| Complex async flows | ❌ | ✅ `switchMap`, `mergeMap` |
+
+---
+
+## 2. Standalone Components
+
+Standalone components are self-contained and don't require NgModule declarations.
+
+### Creating Standalone Components
+
+```typescript
+import { Component } from "@angular/core";
+import { CommonModule } from "@angular/common";
+import { RouterLink } from "@angular/router";
+
+@Component({
+ selector: "app-header",
+ standalone: true,
+ imports: [CommonModule, RouterLink], // Direct imports
+ template: `
+
+ `,
+})
+export class HeaderComponent {}
+```
+
+### Bootstrapping Without NgModule
+
+```typescript
+// main.ts
+import { bootstrapApplication } from "@angular/platform-browser";
+import { provideRouter } from "@angular/router";
+import { provideHttpClient } from "@angular/common/http";
+import { AppComponent } from "./app/app.component";
+import { routes } from "./app/app.routes";
+
+bootstrapApplication(AppComponent, {
+ providers: [provideRouter(routes), provideHttpClient()],
+});
+```
+
+### Lazy Loading Standalone Components
+
+```typescript
+// app.routes.ts
+import { Routes } from "@angular/router";
+
+export const routes: Routes = [
+ {
+ path: "dashboard",
+ loadComponent: () =>
+ import("./dashboard/dashboard.component").then(
+ (m) => m.DashboardComponent,
+ ),
+ },
+ {
+ path: "admin",
+ loadChildren: () =>
+ import("./admin/admin.routes").then((m) => m.ADMIN_ROUTES),
+ },
+];
+```
+
+---
+
+## 3. Zoneless Angular
+
+Zoneless applications don't use zone.js, improving performance and debugging.
+
+### Enabling Zoneless Mode
+
+```typescript
+// main.ts
+import { bootstrapApplication } from "@angular/platform-browser";
+import { provideZonelessChangeDetection } from "@angular/core";
+import { AppComponent } from "./app/app.component";
+
+bootstrapApplication(AppComponent, {
+ providers: [provideZonelessChangeDetection()],
+});
+```
+
+### Zoneless Component Patterns
+
+```typescript
+import { Component, signal, ChangeDetectionStrategy } from "@angular/core";
+
+@Component({
+ selector: "app-counter",
+ standalone: true,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: `
+ Count: {{ count() }}
+
+ `,
+})
+export class CounterComponent {
+ count = signal(0);
+
+ increment() {
+ this.count.update((v) => v + 1);
+ // No zone.js needed - Signal triggers change detection
+ }
+}
+```
+
+### Key Zoneless Benefits
+
+- **Performance**: No zone.js patches on async APIs
+- **Debugging**: Clean stack traces without zone wrappers
+- **Bundle size**: Smaller without zone.js (~15KB savings)
+- **Interoperability**: Better with Web Components and micro-frontends
+
+---
+
+## 4. Server-Side Rendering & Hydration
+
+### SSR Setup with Angular CLI
+
+```bash
+ng add @angular/ssr
+```
+
+### Hydration Configuration
+
+```typescript
+// app.config.ts
+import { ApplicationConfig } from "@angular/core";
+import {
+ provideClientHydration,
+ withEventReplay,
+} from "@angular/platform-browser";
+
+export const appConfig: ApplicationConfig = {
+ providers: [provideClientHydration(withEventReplay())],
+};
+```
+
+### Incremental Hydration (v20+)
+
+```typescript
+import { Component } from "@angular/core";
+
+@Component({
+ selector: "app-page",
+ standalone: true,
+ template: `
+
+
+ @defer (hydrate on viewport) {
+
+ }
+
+ @defer (hydrate on interaction) {
+
+ }
+ `,
+})
+export class PageComponent {}
+```
+
+### Hydration Triggers
+
+| Trigger | When to Use |
+| ---------------- | --------------------------------------- |
+| `on idle` | Low-priority, hydrate when browser idle |
+| `on viewport` | Hydrate when element enters viewport |
+| `on interaction` | Hydrate on first user interaction |
+| `on hover` | Hydrate when user hovers |
+| `on timer(ms)` | Hydrate after specified delay |
+
+---
+
+## 5. Modern Routing Patterns
+
+### Functional Route Guards
+
+```typescript
+// auth.guard.ts
+import { inject } from "@angular/core";
+import { Router, CanActivateFn } from "@angular/router";
+import { AuthService } from "./auth.service";
+
+export const authGuard: CanActivateFn = (route, state) => {
+ const auth = inject(AuthService);
+ const router = inject(Router);
+
+ if (auth.isAuthenticated()) {
+ return true;
+ }
+
+ return router.createUrlTree(["/login"], {
+ queryParams: { returnUrl: state.url },
+ });
+};
+
+// Usage in routes
+export const routes: Routes = [
+ {
+ path: "dashboard",
+ loadComponent: () => import("./dashboard.component"),
+ canActivate: [authGuard],
+ },
+];
+```
+
+### Route-Level Data Resolvers
+
+```typescript
+import { inject } from '@angular/core';
+import { ResolveFn } from '@angular/router';
+import { UserService } from './user.service';
+import { User } from './user.model';
+
+export const userResolver: ResolveFn = (route) => {
+ const userService = inject(UserService);
+ return userService.getUser(route.paramMap.get('id')!);
+};
+
+// In routes
+{
+ path: 'user/:id',
+ loadComponent: () => import('./user.component'),
+ resolve: { user: userResolver }
+}
+
+// In component
+export class UserComponent {
+ private route = inject(ActivatedRoute);
+ user = toSignal(this.route.data.pipe(map(d => d['user'])));
+}
+```
+
+---
+
+## 6. Dependency Injection Patterns
+
+### Modern inject() Function
+
+```typescript
+import { Component, inject } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { UserService } from './user.service';
+
+@Component({...})
+export class UserComponent {
+ // Modern inject() - no constructor needed
+ private http = inject(HttpClient);
+ private userService = inject(UserService);
+
+ // Works in any injection context
+ users = toSignal(this.userService.getUsers());
+}
+```
+
+### Injection Tokens for Configuration
+
+```typescript
+import { InjectionToken, inject } from "@angular/core";
+
+// Define token
+export const API_BASE_URL = new InjectionToken("API_BASE_URL");
+
+// Provide in config
+bootstrapApplication(AppComponent, {
+ providers: [{ provide: API_BASE_URL, useValue: "https://api.example.com" }],
+});
+
+// Inject in service
+@Injectable({ providedIn: "root" })
+export class ApiService {
+ private baseUrl = inject(API_BASE_URL);
+
+ get(endpoint: string) {
+ return this.http.get(`${this.baseUrl}/${endpoint}`);
+ }
+}
+```
+
+---
+
+## 7. Component Composition & Reusability
+
+### Content Projection (Slots)
+
+```typescript
+@Component({
+ selector: 'app-card',
+ template: `
+
+ `
+})
+export class CardComponent {}
+
+// Usage
+
+ Title
+ Body content
+
+```
+
+### Host Directives (Composition)
+
+```typescript
+// Reusable behaviors without inheritance
+@Directive({
+ standalone: true,
+ selector: '[appTooltip]',
+ inputs: ['tooltip'] // Signal input alias
+})
+export class TooltipDirective { ... }
+
+@Component({
+ selector: 'app-button',
+ standalone: true,
+ hostDirectives: [
+ {
+ directive: TooltipDirective,
+ inputs: ['tooltip: title'] // Map input
+ }
+ ],
+ template: ``
+})
+export class ButtonComponent {}
+```
+
+---
+
+## 8. State Management Patterns
+
+### Signal-Based State Service
+
+```typescript
+import { Injectable, signal, computed } from "@angular/core";
+
+interface AppState {
+ user: User | null;
+ theme: "light" | "dark";
+ notifications: Notification[];
+}
+
+@Injectable({ providedIn: "root" })
+export class StateService {
+ // Private writable signals
+ private _user = signal(null);
+ private _theme = signal<"light" | "dark">("light");
+ private _notifications = signal([]);
+
+ // Public read-only computed
+ readonly user = computed(() => this._user());
+ readonly theme = computed(() => this._theme());
+ readonly notifications = computed(() => this._notifications());
+ readonly unreadCount = computed(
+ () => this._notifications().filter((n) => !n.read).length,
+ );
+
+ // Actions
+ setUser(user: User | null) {
+ this._user.set(user);
+ }
+
+ toggleTheme() {
+ this._theme.update((t) => (t === "light" ? "dark" : "light"));
+ }
+
+ addNotification(notification: Notification) {
+ this._notifications.update((n) => [...n, notification]);
+ }
+}
+```
+
+### Component Store Pattern with Signals
+
+```typescript
+import { Injectable, signal, computed, inject } from "@angular/core";
+import { HttpClient } from "@angular/common/http";
+import { toSignal } from "@angular/core/rxjs-interop";
+
+@Injectable()
+export class ProductStore {
+ private http = inject(HttpClient);
+
+ // State
+ private _products = signal([]);
+ private _loading = signal(false);
+ private _filter = signal("");
+
+ // Selectors
+ readonly products = computed(() => this._products());
+ readonly loading = computed(() => this._loading());
+ readonly filteredProducts = computed(() => {
+ const filter = this._filter().toLowerCase();
+ return this._products().filter((p) =>
+ p.name.toLowerCase().includes(filter),
+ );
+ });
+
+ // Actions
+ loadProducts() {
+ this._loading.set(true);
+ this.http.get("/api/products").subscribe({
+ next: (products) => {
+ this._products.set(products);
+ this._loading.set(false);
+ },
+ error: () => this._loading.set(false),
+ });
+ }
+
+ setFilter(filter: string) {
+ this._filter.set(filter);
+ }
+}
+```
+
+---
+
+## 9. Forms with Signals (Coming in v22+)
+
+### Current Reactive Forms
+
+```typescript
+import { Component, inject } from "@angular/core";
+import { FormBuilder, Validators, ReactiveFormsModule } from "@angular/forms";
+
+@Component({
+ selector: "app-user-form",
+ standalone: true,
+ imports: [ReactiveFormsModule],
+ template: `
+
+ `,
+})
+export class UserFormComponent {
+ private fb = inject(FormBuilder);
+
+ form = this.fb.group({
+ name: ["", Validators.required],
+ email: ["", [Validators.required, Validators.email]],
+ });
+
+ onSubmit() {
+ if (this.form.valid) {
+ console.log(this.form.value);
+ }
+ }
+}
+```
+
+### Signal-Aware Form Patterns (Preview)
+
+```typescript
+// Future Signal Forms API (experimental)
+import { Component, signal } from '@angular/core';
+
+@Component({...})
+export class SignalFormComponent {
+ name = signal('');
+ email = signal('');
+
+ // Computed validation
+ isValid = computed(() =>
+ this.name().length > 0 &&
+ this.email().includes('@')
+ );
+
+ submit() {
+ if (this.isValid()) {
+ console.log({ name: this.name(), email: this.email() });
+ }
+ }
+}
+```
+
+---
+
+## 10. Performance Optimization
+
+### Change Detection Strategies
+
+```typescript
+@Component({
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ // Only checks when:
+ // 1. Input signal/reference changes
+ // 2. Event handler runs
+ // 3. Async pipe emits
+ // 4. Signal value changes
+})
+```
+
+### Defer Blocks for Lazy Loading
+
+```typescript
+@Component({
+ template: `
+
+
+
+
+ @defer (on viewport) {
+
+ } @placeholder {
+
+ } @loading (minimum 200ms) {
+
+ } @error {
+ Failed to load chart
+ }
+ `
+})
+```
+
+### NgOptimizedImage
+
+```typescript
+import { NgOptimizedImage } from '@angular/common';
+
+@Component({
+ imports: [NgOptimizedImage],
+ template: `
+
+
+
+ `
+})
+```
+
+---
+
+## 11. Testing Modern Angular
+
+### Testing Signal Components
+
+```typescript
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { CounterComponent } from "./counter.component";
+
+describe("CounterComponent", () => {
+ let component: CounterComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [CounterComponent], // Standalone import
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(CounterComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it("should increment count", () => {
+ expect(component.count()).toBe(0);
+
+ component.increment();
+
+ expect(component.count()).toBe(1);
+ });
+
+ it("should update DOM on signal change", () => {
+ component.count.set(5);
+ fixture.detectChanges();
+
+ const el = fixture.nativeElement.querySelector(".count");
+ expect(el.textContent).toContain("5");
+ });
+});
+```
+
+### Testing with Signal Inputs
+
+```typescript
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { ComponentRef } from "@angular/core";
+import { UserCardComponent } from "./user-card.component";
+
+describe("UserCardComponent", () => {
+ let fixture: ComponentFixture;
+ let componentRef: ComponentRef;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [UserCardComponent],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(UserCardComponent);
+ componentRef = fixture.componentRef;
+
+ // Set signal inputs via setInput
+ componentRef.setInput("id", "123");
+ componentRef.setInput("name", "John Doe");
+
+ fixture.detectChanges();
+ });
+
+ it("should display user name", () => {
+ const el = fixture.nativeElement.querySelector("h3");
+ expect(el.textContent).toContain("John Doe");
+ });
+});
+```
+
+---
+
+## Best Practices Summary
+
+| Pattern | ✅ Do | ❌ Don't |
+| -------------------- | ------------------------------ | ------------------------------- |
+| **State** | Use Signals for local state | Overuse RxJS for simple state |
+| **Components** | Standalone with direct imports | Bloated SharedModules |
+| **Change Detection** | OnPush + Signals | Default CD everywhere |
+| **Lazy Loading** | `@defer` and `loadComponent` | Eager load everything |
+| **DI** | `inject()` function | Constructor injection (verbose) |
+| **Inputs** | `input()` signal function | `@Input()` decorator (legacy) |
+| **Zoneless** | Enable for new projects | Force on legacy without testing |
+
+---
+
+## Resources
+
+- [Angular.dev Documentation](https://angular.dev)
+- [Angular Signals Guide](https://angular.dev/guide/signals)
+- [Angular SSR Guide](https://angular.dev/guide/ssr)
+- [Angular Update Guide](https://angular.dev/update-guide)
+- [Angular Blog](https://blog.angular.dev)
+
+---
+
+## Common Troubleshooting
+
+| Issue | Solution |
+| ------------------------------ | --------------------------------------------------- |
+| Signal not updating UI | Ensure `OnPush` + call signal as function `count()` |
+| Hydration mismatch | Check server/client content consistency |
+| Circular dependency | Use `inject()` with `forwardRef` |
+| Zoneless not detecting changes | Trigger via signal updates, not mutations |
+| SSR fetch fails | Use `TransferState` or `withFetch()` |
diff --git a/skills/angular/metadata.json b/skills/angular/metadata.json
new file mode 100644
index 00000000..13da2801
--- /dev/null
+++ b/skills/angular/metadata.json
@@ -0,0 +1,14 @@
+{
+ "version": "1.0.0",
+ "organization": "Antigravity Awesome Skills",
+ "date": "February 2026",
+ "abstract": "Comprehensive guide to modern Angular development (v20+) designed for AI agents and LLMs. Covers Signals, Standalone Components, Zoneless applications, SSR/Hydration, reactive patterns, routing, dependency injection, and modern forms. Emphasizes component-driven architecture with practical examples and migration strategies for modernizing existing codebases.",
+ "references": [
+ "https://angular.dev",
+ "https://angular.dev/guide/signals",
+ "https://angular.dev/guide/zoneless",
+ "https://angular.dev/guide/ssr",
+ "https://angular.dev/guide/standalone-components",
+ "https://angular.dev/guide/defer"
+ ]
+}