Angular Conventions
ASecurityAngular 22+ conventions and the single entry point for Angular work: components, signals, DI with inject(), Signal Forms, httpResource(), routing and guards, NgRx Signal Store, directives, overlays and dialogs, MSAL auth, A2UI generative UI, Vitest testing, CSS that will not apply, data-table column geometry, bundle-size optimization, runtime config injection, drag-and-drop reordering, and file downloads. Use when the task is Angular-specific but not yet narrowed to one topic, or to route int...
- 4 stars
- 0 votes
- 0 copies
- 1 view
- Added September 9, 2026
Works with
Security analysis
100/100Pro scans all 20 files and shows the line behind each finding
npx -y skills add alexander-kastil/agentic-sw-engineering --skill angular-conventions --agent claude-codeAre you the author of Angular Conventions?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/alexander-kastil-angular-conventions)---
name: angular-conventions
description: 'Angular 22+ conventions and the single entry point for Angular work: components, signals, DI with inject(), Signal Forms, httpResource(), routing and guards, NgRx Signal Store, directives, overlays and dialogs, MSAL auth, A2UI generative UI, Vitest testing, CSS that will not apply, data-table column geometry, bundle-size optimization, runtime config injection, drag-and-drop reordering, and file downloads. Use when the task is Angular-specific but not yet narrowed to one topic, or to route into a specific Angular concern. Triggers on Angular architecture, standalone component, inject(), signal(), computed(), signal forms, httpResource(), route guard, MSAL login does nothing, interaction_in_progress, duplicate HTTP requests, initial budget exceeded, css change has no effect, container query not working, table shifts on sort, column picker, empty aria-live gap, runtime config injection, reorder cards, download a file, TestBed, vi.mock, ng test.'
---
# Angular Conventions
Group reusable Angular guidance under one entry skill.
## When to Use This Skill
- The task is Angular-specific but not yet narrowed to one Angular topic.
- You need a single entry point for Angular components, DI, forms, HTTP, routing, or signals.
- The repository has its own Angular rules in `docs/` and you want to pair those with general Angular conventions.
## Defaults
| Topic | Current rule |
| --------------------------- | ------------------------------------------------------------------------------------------ |
| Angular version | Angular `22.x` or later |
| Architecture | Standalone components — do NOT set `standalone: true` (default since v20) |
| Change detection | `ChangeDetectionStrategy.OnPush` on every component |
| Dependency injection | `inject()` function only — never constructor parameters |
| Inputs / Outputs | `input()` / `output()` signals — never `@Input()` / `@Output()` decorators |
| Host bindings | `host` object in `@Component` / `@Directive` — never `@HostBinding` / `@HostListener` |
| Template control flow | `@if` / `@for` / `@switch` — never `*ngIf` / `*ngFor` / `*ngSwitch` |
| Class and style bindings | `[class.x]` / `[style.x]` — never `ngClass` / `ngStyle` |
| Local state | `signal()` — never `BehaviorSubject` |
| State management | **NgRx Signal Store (`@ngrx/signals`) is required** for shared/server state — `signalStoreFeature()` slices with `withState`/`withComputed`/`withMethods`, async via `rxMethod` + `tapResponse` + `patchState`. `signal()` is for component-local UI state only; never `BehaviorSubject` stores or class-based `@ngrx/store` reducers. |
| HTTP reads | `httpResource()` for reactive reads; `HttpClient` for mutations |
| Forms | Signal Forms (`form()` + `[formField]`) — never Reactive Forms or `ngModel` |
| Scaffolding | Angular CLI for components, services, directives, and pipes |
| Project structure | Feature-based folder organization; flat feature folders for smaller apps |
| Routing | `app.routes.ts` with functional guards and lazy-loaded feature routes |
| Component file layout | Separate `.ts`, `.html`, `.css` files; kebab-case names; one folder per component |
| Component organization | Functional hierarchy: feature folder → component sub-folder |
## Usage Note
- This skill captures shared Angular 22 conventions. Always check the target repository's own `docs/` or `CLAUDE.md` for repo-specific overrides (API base URLs, store architecture, auth provider, money conventions, etc.) before applying these defaults.
## Component File Organization
Angular projects use a functional storage hierarchy:
```
app/
<feature>/ ← domain/feature folder (auth, person, cart, …)
<component-name>/ ← one folder per component
<component-name>.ts
<component-name>.html (if separate template)
<component-name>.spec.ts
<service>.ts ← services live at feature level, not in a sub-folder
shared/ ← cross-cutting UI components used across features
<shared-component>/
store/ ← global state (NgRx store, signal stores)
```
Rules:
- Each component gets its own folder named after the component.
- Files inside the folder share the component's name (e.g., `user-card/user-card.ts`).
- Services are placed at the feature level, not inside a component sub-folder.
- `shared/` contains components reused across multiple features.
- `store/` contains global state artifacts.
## Delegate Map
| Request type | Reference to use |
| ----------------------------------------------------- | ------------------------------------------------------------------ |
| Build or refactor standalone components | [`angular-component`](references/angular-component.md) |
| Component folder layout and file organization | [`angular-component`](references/angular-component.md) |
| Configure dependency injection or providers | [`angular-di`](references/angular-di.md) |
| Build forms and validation flows | [`angular-forms`](references/angular-forms.md) |
| Implement API calls and data loading | [`angular-http`](references/angular-http.md) |
| Download a file the API generates, with auth | [`angular-file-download`](references/angular-file-download.md) |
| Configure navigation and route behavior | [`angular-routing`](references/angular-routing.md) |
| Model reactive state with signals | [`angular-signals`](references/angular-signals.md) |
| Design app state so one composed signal store fully serves the app (feature-per-domain, container/presenter binding) | [`angular-signal-store-design`](references/angular-signal-store-design.md) |
| Add a global store-driven activity indicator (HTTP request-counter + explicit AI-activity flag + interceptor + progress bar) | [`angular-activity-indicator`](references/angular-activity-indicator.md) |
| Load-once data loading with local CRUD sync (ensure*Loaded guards, in-flight dedupe/shareReplay, resolvers scoped to what a route reads, mutations patch the store instead of refetching, sessionStorage TTL cache, duplicate-request elimination) | [`angular-load-once-store`](references/angular-load-once-store.md) |
| Write a spec for a component, service, guard or signal with Vitest and `TestBed` | [`angular-testing`](references/angular-testing.md) |
| Run or scope a Vitest suite and read the result (`ng test` vs raw `npx vitest`, `--include` vs `--filter`, a run that skipped everything but shows no red, one broken spec failing the whole type-check, which failing file to fix first) | [`angular-test-execution`](references/angular-test-execution.md) |
| Measure coverage and rank the gaps (`@vitest/coverage-v8` not installed, where `coverage-summary.json` actually lands, a `.html` file at ~0% functions, missing `<name>.spec.ts` is not missing coverage) | [`angular-test-coverage`](references/angular-test-coverage.md) |
| The spec fails for a reason that is not the code under test: `vi.mock` factory traps (`__spreadValues is not a function`, mock is `undefined`, `is not a constructor`, `vi.hoisted`), `expectOne` finding no matching request when the URL has query params, flushing the wrong body type for a blob request, one leaked request cascading into dozens of failures | [`angular-test-doubles`](references/angular-test-doubles.md) |
| A plain-bound native `<select>` renders with nothing selected (no `ngModel`/`formField`): `[value]` on the select loses to the options' own bindings, put `[selected]` on the options | [`angular-select-binding`](references/angular-select-binding.md) |
| Build modals/dialogs/popups, ESC-to-close, dark native controls | [`angular-overlays`](references/angular-overlays.md) |
| Build a draggable/resizable two-pane splitter (`ux-splitter`) | [`angular-draggable-splitter`](references/angular-draggable-splitter.md) |
| Stable data-table column geometry (columns jump when sorting, table shifts on sort/filter, `table-fixed` vs `table-auto`, columns overflow the container, last column pushed off-screen, unwanted horizontal scrollbar, server-side sort with infinite scroll, collapsible nav rail changes available width at a fixed viewport, container query instead of media query, sidebar expanded vs collapsed, iPad Pro 13 inch) | [`angular-table-column-layout`](references/angular-table-column-layout.md) |
| User-resizable table columns (drag column border, resize column width, column resize handle, drag fires the sort by mistake, flexible column collapses to a few px, persist column widths, stale stored widths override new defaults) | [`angular-table-column-resize`](references/angular-table-column-resize.md) |
| Show/hide table columns (column picker, "Spalten" dropdown, hide a column, hidden by default, column visibility, explicit choice vs responsive breakpoint, checkbox stays checked below breakpoint, cannot hide last column) | [`angular-table-column-visibility`](references/angular-table-column-visibility.md) |
| Drag and drop for grids or reorderable lists without a library (cdkDrag alternative, reorder rows, draggable grid, drop target, not-allowed drop) | [`angular-drag-drop`](references/angular-drag-drop.md) |
| Build a bottom sheet with no Angular Material/CDK (native `<dialog>`, viewport-anchored, slide-up, imperative open()/close()) | [`angular-bottom-sheet`](references/angular-bottom-sheet.md) |
| Build a file drop zone / drag-and-drop file upload (dragover state, hidden input, single vs multi, `filesSelected` output, store-event variant) | [`angular-file-dropzone`](references/angular-file-dropzone.md) |
| Build a multi-step file-import wizard (upload → review/column-map → AI-assist → commit; draft in the store, no client-side parsing) | [`file-import-wiz`](references/file-import-wiz.md) |
| Blob download or a retained-documents list (blob download, document list, file download, object URL, retained documents, import documents; `HttpClient` blob → `createObjectURL` → anchor click) | [`blob-document-list`](references/blob-document-list.md) |
| Build a user & permission admin UI (RBAC: admin-users, admin-permissions, user/roles dialogs) backed by mixed auth (Entra MSAL + local JWT), with `withAuth`/`withAdmin` signal-store slices, a local-first token interceptor, and an `adminGuard` — admin UI, permission matrix, roles screen, RBAC UI, local login form, adminEmails | [`angular-users-permissions-admin`](references/angular-users-permissions-admin.md) |
| Build a collapsible/expandable disclosure panel or accordion, especially inside an equal-height card grid (collapsed by default, show more, panel will not expand, cards same height, `0fr`/`1fr` row animation) | [`angular-disclosure-panels`](references/angular-disclosure-panels.md) |
| Give a set of pages one shared chrome: eyebrow + H1 hero, KPI band, routed tab nav; fix two stacked headlines ("double headline"), convert stacked sections into routed tabs, replace explanatory prose with KPI tiles, share CSS primitives without leaking through global `styles.css` | [`angular-page-chrome`](references/angular-page-chrome.md) |
| A CSS edit has no visible effect: diagnose via the CSSOM, a stale `ng serve` bundle, a `@container` rule that cannot match its own container, an unlayered global beating a Tailwind utility, a class living in another component's stylesheet, or a `font-weight` the loaded font file cannot honour (prove the face is variable via `fvar` before declaring a weight), or a production build whose inlined critical CSS is blocked by the app's own CSP so the deployed page ships completely unstyled; choose `flex-wrap` over a container query for "sits beside, else wraps below" | [`angular-css-not-applying`](references/angular-css-not-applying.md) |
| Data the SPA ships as static files under `public/` is readable without login (the route guard protects the route, not the file): how to check, why basic auth breaks the app's own `fetch()`, why the edge cannot validate an MSAL token, the `Sec-Fetch-Dest`/`Sec-Fetch-Site` nginx and Caddy mitigation, and its honest obscurity-grade limits | [`spa-static-data-exposure`](references/spa-static-data-exposure.md) |
| Unexplained vertical space in a `gap`-spaced column: an always-rendered but currently empty `aria-live` wrapper (or any conditionally-empty child) still eats a full gap — `empty:absolute` vs `empty:hidden`, and why `:empty` still matches through an `@if` anchor | [`angular-live-region-layout`](references/angular-live-region-layout.md) |
| Inspect the RUNNING app's DI graph or signal graph from the browser (which providers actually resolved, is the interceptor registered once, did MSAL_INSTANCE resolve, is zoneless really on, why does this effect re-run, `angular:di_graph`, `angular:signal_graph`, `devtoolstooldiscovery`, `list_3p_developer_tools`, `categoryExperimentalThirdParty`, converting circular structure to JSON) | [`angular-runtime-graph-inspection`](references/angular-runtime-graph-inspection.md) |
| Spot or fix anti-patterns / legacy code | [`angular-antipatterns`](references/angular-antipatterns.md) |
| Wire MSAL providers: interaction-type rule, `environment` config, the providers factory, `protectedResourceMap` keys, `app.config.ts` registration | [`angular-msal-auth`](references/angular-msal-auth.md) |
| MSAL application patterns: a cached `AuthService`, functional guards in both directions, local-only vs full logout, one `authEnabled` flag across SPA and API | [`msal-auth-patterns`](references/msal-auth-patterns.md) |
| MSAL is wired but misbehaving: blank page when auth is enabled, redirect loop through Microsoft, missing `Authorization` header, 401 triage, popup failure, Easy Auth conflict, the Login button that silently does nothing (an unsubscribed `loginRedirect()` swallowing `interaction_in_progress`) | [`msal-troubleshooting`](references/msal-troubleshooting.md) |
| Which `@azure/msal-angular` major to run and what each hop broke: v4/v5/v6 API changes, where `navigateToLoginRequestUrl` moved, the removed-API grep checklist | [`msal-version-changes`](references/msal-version-changes.md) |
| MSAL client wiring deep-dive: provider setup, `APP_INITIALIZER`, `AuthStateService`, single-bootstrap rules, the migration checklist, and whether the COOP redirect-bridge page is needed at all | [`msal-angular`](references/msal-angular.md) |
| Entra app registration via Azure CLI: create from scratch, redirect URIs, platform types, `requestedAccessTokenVersion` and the pre-authorization chicken-and-egg | [`msal-angular-appreg`](references/msal-angular-appreg.md) |
| Upgrade or update an Angular app to the latest version, and the compatibility gate for deciding what NOT to bump (TypeScript peer range off `@angular/compiler-cli`, stable-with-unmet-peer vs an RC, README support matrices no tool can see, test-env majors, the Node engine floor and where it is actually pinned, why caret floors are the deployed version when the Dockerfile has no lockfile). Run the gate first with `node <this-skill>/scripts/angular-update-preflight.mjs --root <dir>` | [`angular-update`](references/angular-update.md) |
| Fix a failing production bundle-size budget: measure eager vs lazy chunks, non-ESM tree-shaking traps, raw-vs-gzip budgets, lazy-init a heavy lib via dynamic `import()` (reduce bundle, main.js too big, initial budget exceeded, tree-shaking, lazy load) | [`angular-bundle-optimization`](references/angular-bundle-optimization.md) |
| Decide whether to adopt a third-party library at all: is it maintained, is the Angular wrapper dead, how many kB does it really cost against the current budget, and will it round-trip our content (is library X worth it, which markdown/rich-text/chart/date library, npmjs.com returns 403, registry.npmjs.org, weekly downloads, last publish, ngx wrapper unmaintained, use the library directly, measure gzip with esbuild, WYSIWYG mangles shortcodes or raw HTML or front matter) | [`angular-dependency-evaluation`](references/angular-dependency-evaluation.md) |
| Move per-environment values out of the bundle so one build serves every environment: read `globalThis.__APP_CONFIG__` in `environment.ts` with `??` fallbacks, load `config.js` from `index.html`, and retire the per-slot `environment.<slot>.ts` + `fileReplacements` (runtime config injection, config.js, `__APP_CONFIG__`, apiBase baked into the bundle, one bundle many environments, environment.blue.ts, fileReplacements per slot, slot-agnostic image, promote by retag) | [`angular-runtime-config`](references/angular-runtime-config.md) |
| Migrate markdown-renderer into a split demo-container layout | [`angular-migrate-markdown`](references/angular-migrate-markdown.md) |
| Render agent-driven generative UI with the A2UI protocol (catalogs, surfaces, actions, theming) | [`angular-a2ui`](references/angular-a2ui.md) |
| Build a multi-step wizard / setup flow: full-width step-rail shell, phase-driven active/done states, two-column step body, copy-to-clipboard, real-data chip filters | [`angular-wizard`](references/angular-wizard.md) |
## Example Prompts
- "Use angular-conventions to choose the right pattern for a new Angular form."
- "Route this Angular data-loading task to the right conventions reference."
- "Use angular-conventions for a component and signals refactor."
- "Use angular-conventions to design a feature-per-domain signal store that fully serves the app."
- "Use angular-conventions to add a global store-driven activity indicator (HTTP + AI activity) with an interceptor."
- "Use angular-conventions to scaffold a standalone Angular component and confirm the right CLI command."
- "Use angular-conventions to upgrade this workspace to the latest Angular version."
- "Use angular-conventions to migrate markdown-renderer into the split demo-container layout."
- "Use angular-conventions to wire an A2UI renderer and custom catalog into an Angular client."
- "Use angular-conventions to add a drag-and-drop file drop zone that emits selected files to the store."
- "Use angular-conventions to build a file-import wizard (upload → review → commit) for Mieterlisten."
- "Use angular-conventions to add a retained-documents list with a blob download button."
## Layout Rules
| Rule | Detail |
| ---- | ------ |
| Button alignment | All button groups (`.actions`, `.section-actions`, `.hero-actions`) must use `justify-content: flex-end` so buttons sit at the right edge |
| Default button style | Orange fill (`var(--accent)`) with black text (`#0a0c10`), `font-weight: 700`. This is the global default — no extra class needed |
| Destructive buttons | Stop, Delete, Remove, Drop — use `.btn-danger`: solid red fill (`var(--danger)`) with black text (`#0a0c10`) |
| Exceptions | Icon/utility buttons (`.btn-close`, `.tree-btn`, `.template-card`) keep their own component-scoped overrides |
## Project-Specific Rule
- Use this skill for reusable Angular guidance.
- Check the repository's `docs/` for project-specific architecture, folder layout, naming, and build rules before applying these conventions.
## Code Generation
Use Angular CLI rather than hand-writing the initial file structure.
```bash
ng generate component my-component
ng generate service my-service
ng generate directive my-directive
ng generate pipe my-pipe
```
## Development Commands
| Command | Use |
| ------------------ | ------------------------------------------ |
| `ng serve` | Start the local Angular development server |
| `ng build` | Create a production build |
| `ng build --watch` | Rebuild continuously during development |
## Deployment Note
Angular projects in this repository are typically deployed to Azure Static Web Apps or Azure Container Apps. Use the project-specific deployment scripts and documentation instead of inventing a new deployment path.
## NDJSON Streaming into Signal Store
When a store feature must consume a server-sent NDJSON stream:
1. Use `fetch` (not `HttpClient`) to POST and obtain `response.body.getReader()`.
2. Decode chunks with `new TextDecoder()` in streaming mode; split on `\n`; parse each non-empty line as JSON.
3. Drive `patchState` for each event type (`stage`, `cost`, `result`) as lines arrive.
4. Fall back to an `HttpClient` call on any `fetch` rejection, non-200 response, or absent body.
5. Put the fallback in a separate async function and call it from both the `catch` block and the non-ok guard.
6. Revoke any object-URL (`URL.revokeObjectURL`) in both success and error paths.
Reference implementation: `src/inventory-ui/src/app/store/features/with-scan.feature.ts` (`resolvePhotoStream`).
Files in this skill
- SKILL.md
- references/angular-a2ui.md
- references/angular-activity-indicator.md
- references/angular-antipatterns.md
- references/angular-bottom-sheet.md
- references/angular-bundle-optimization.md
- references/angular-component-patterns.md
- references/angular-component.md
- references/angular-css-not-applying.md
- references/angular-dependency-evaluation.md
- references/angular-di-patterns.md
- references/angular-di.md
- references/angular-disclosure-panels.md
- references/angular-drag-drop.md
- references/angular-draggable-splitter.md
- references/angular-file-download.md
- references/angular-file-dropzone.md
- references/angular-form-patterns.md
- references/angular-form-value-control-patterns.md
- references/angular-forms.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.