@c8y/websdk 1023.81.3 → 1023.82.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +111 -0
  2. package/dist/ng-add/ai-tools-configure.d.ts +10 -0
  3. package/dist/ng-add/ai-tools-configure.js +318 -0
  4. package/dist/ng-add/ai-tools-configure.js.map +1 -0
  5. package/dist/ng-add/index.js +60 -0
  6. package/dist/ng-add/index.js.map +1 -1
  7. package/dist/ng-add/ng-add.model.d.ts +1 -0
  8. package/dist/templates/ai-configs/claude/CLAUDE.md +98 -0
  9. package/dist/templates/ai-configs/claude/rules/e2e-tests.instructions.md +187 -0
  10. package/dist/templates/ai-configs/claude/rules/frontend.instructions.md +251 -0
  11. package/dist/templates/ai-configs/claude/rules/unit-tests.instructions.md +172 -0
  12. package/dist/templates/ai-configs/cursor/cursor.mdc +98 -0
  13. package/dist/templates/ai-configs/cursor/rules/e2e-tests.instructions.md +187 -0
  14. package/dist/templates/ai-configs/cursor/rules/frontend.instructions.md +251 -0
  15. package/dist/templates/ai-configs/cursor/rules/unit-tests.instructions.md +172 -0
  16. package/dist/templates/ai-configs/gemini/GEMINI.md +98 -0
  17. package/dist/templates/ai-configs/gemini/rules/e2e-tests.instructions.md +181 -0
  18. package/dist/templates/ai-configs/gemini/rules/frontend.instructions.md +247 -0
  19. package/dist/templates/ai-configs/gemini/rules/unit-tests.instructions.md +166 -0
  20. package/dist/templates/ai-configs/github/copilot-instructions.md +98 -0
  21. package/dist/templates/ai-configs/github/instructions/e2e-tests.instructions.md +187 -0
  22. package/dist/templates/ai-configs/github/instructions/frontend.instructions.md +251 -0
  23. package/dist/templates/ai-configs/github/instructions/unit-tests.instructions.md +172 -0
  24. package/dist/templates/ai-configs/jetbrains/guidelines.md +98 -0
  25. package/dist/templates/ai-configs/jetbrains/rules/e2e-tests.instructions.md +181 -0
  26. package/dist/templates/ai-configs/jetbrains/rules/frontend.instructions.md +247 -0
  27. package/dist/templates/ai-configs/jetbrains/rules/unit-tests.instructions.md +166 -0
  28. package/dist/templates/ai-configs/windsurf/guidelines.md +98 -0
  29. package/dist/templates/ai-configs/windsurf/rules/e2e-tests.instructions.md +187 -0
  30. package/dist/templates/ai-configs/windsurf/rules/frontend.instructions.md +251 -0
  31. package/dist/templates/ai-configs/windsurf/rules/unit-tests.instructions.md +172 -0
  32. package/package.json +3 -2
@@ -0,0 +1,98 @@
1
+ ## Project Identity
2
+
3
+ **Cumulocity IoT Web Application** — custom web frontend built with the Cumulocity Web SDK. Extends the Cumulocity IoT platform with custom features and integrations.
4
+
5
+ - **Stack:** Angular 20, TypeScript 5.9.3, RxJS 7.8, Jest 30, Cypress 15, ESLint (airbnb-base + angular-eslint)
6
+ - **Core Libraries:** `@c8y/ngx-components`, `@c8y/client`, `@c8y/toolkit`, `@c8y/devkit`, `@c8y/bootstrap`
7
+ - **Structure:** Standard Angular CLI application scaffolded with `ng add @c8y/websdk`
8
+
9
+ ---
10
+
11
+ ## 🛑 MANDATORY WORKFLOW: UI Planning & Implementation
12
+
13
+ **STOP: Before analyzing, planning, designing, OR implementing ANY UI feature/component, you MUST complete this workflow. No exceptions.**
14
+
15
+ ### Step 1: Fetch Codex Documentation (REQUIRED BEFORE PLANNING)
16
+
17
+ When the user requests ANY UI work (planning, design, implementation), your **FIRST ACTION** must be:
18
+
19
+ 1. **Fetch** https://cumulocity.com/codex/llms.txt
20
+ 2. **Search** the content for relevant component keywords (modal, button, form, table, etc.)
21
+ 3. **If found:** Read the specific `.md` documentation file like `https://cumulocity.com/codex/components/forms/editor.md`
22
+ 4. **Read and analyze** the documentation content
23
+ 5. **Locate examples** in the public tutorial repository:
24
+ e.g. for file path './packages/tutorial/src/selector/asset-selector-example/tree-options/asset-selector-tree-example.component.ts' get file from
25
+ 'https://github.com/Cumulocity-IoT/tutorial/tree/main/src/selector/asset-selector-example/tree-options/asset-selector-tree-example.component.ts'
26
+ 6. **Read example code** to understand the official pattern
27
+
28
+ **⛔ INVALID:**
29
+ - ❌ "I'll list the Codex URL as a resource to review later"
30
+ - ❌ "The plan should include fetching the documentation"
31
+ - ❌ "Before implementation, we need to review the Codex"
32
+ - ❌ Making a plan without actually fetching and reading documentation
33
+
34
+ **✅ VALID:**
35
+ - ✓ Fetch llms.txt → Find modal docs → Read content → Find tutorial examples → Read example code → Then create plan
36
+
37
+ ### Step 2: Check for Existing Components
38
+
39
+ After reviewing documentation:
40
+
41
+ 1. Search `@c8y/ngx-components` for existing implementations
42
+ 2. Use a matching Cumulocity component if one exists — **never build custom equivalents**
43
+ 3. Only proceed with custom solution if nothing suitable exists
44
+
45
+ ### Step 3: Plan or Implement
46
+
47
+ Only after completing Steps 1-2, proceed with planning or implementation.
48
+
49
+ ---
50
+
51
+ ## Universal Rules — TypeScript
52
+
53
+ - Target is ES2022; do not use syntax unavailable in ES2022
54
+ - Enable strict mode where possible; use explicit types or `unknown` with narrowing guards
55
+ - Avoid `any`; prefer explicit types
56
+ - Use path aliases for imports: `@c8y/ngx-components`, `@c8y/client`, `@c8y/devkit`, `@c8y/options`
57
+ - `experimentalDecorators: true` is set globally; Angular decorator syntax is valid as-is
58
+
59
+ ## Universal Rules — Cumulocity Platform
60
+
61
+ ### Other rules
62
+
63
+ - Use `@c8y/client` services for all Cumulocity REST calls — do **not** use `HttpClient` directly for platform endpoints
64
+ - Check `@c8y/ngx-components` before building custom UI components — it exports a large shared library. Search by visual functionality in [Codex](https://cumulocity.com/codex)
65
+ - Wrap every user-facing string with `C8yTranslatePipe` (`| translate`) or `TranslateService`:
66
+ - ✅ `{{ 'Save' | translate }}` ❌ `Save`
67
+
68
+ ## Angular Patterns
69
+
70
+ - All new components must be **standalone** (`standalone: true`) — do not create NgModule-based components
71
+ - Prefer `ChangeDetectionStrategy.OnPush` for new components (no project-wide enforcement, but scale demands it)
72
+ - Constructor injection and `inject()` function are both present; either is acceptable
73
+ - Manage subscriptions with `async` pipe or `takeUntilDestroyed()` — avoid bare `unsubscribe()` without `ngOnDestroy`
74
+ - Use signals for local component state
75
+ - Consider extracting child components when template exceeds ~150 lines or class exceeds ~200 lines
76
+
77
+ ## Code Quality (PR Review Focus)
78
+
79
+ - Remove `console.log` / `console.debug` before commit; `console.error` acceptable in caught exceptions
80
+ - Flag methods exceeding ~40 lines — consider decomposition
81
+ - Remove commented-out code; use git history for recovery
82
+ - New features should have `*.spec.ts` unit tests; new user flows should have Cypress coverage
83
+ - Document types, methods and properties that are not self-explanatory; don't explain ones that are obvious
84
+ - Test use cases that are truly worth testing, do not create test cases just for the sake of tests volume or coverate
85
+
86
+ ## Security Checks
87
+
88
+ - Flag hardcoded tenant IDs, device IDs, credentials, or API tokens — use `Cypress.env()` in tests, env config in production
89
+ - Review components rendering dynamic device/asset data for XSS: prefer `{{ }}` over `[innerHTML]` with unescaped values
90
+ - Treat IoT payload data as untrusted — validate and sanitize before display
91
+ - Do not expose internal hostnames or credentials in client-side code
92
+
93
+ ## What NOT to Flag
94
+
95
+ - **Import order** — managed by ESLint
96
+ - **Quote style / semicolons** — ESLint enforces single quotes and required semicolons
97
+ - **Trailing commas on function parameters** — ESLint explicitly disallows them
98
+ - **`skipLibCheck: true`** in tsconfig — intentional project setting
@@ -0,0 +1,187 @@
1
+ ---
2
+ name: "Cypress E2E & Component Testing Guide"
3
+ description: "Instructions for Cypress end-to-end and component tests in Cumulocity applications"
4
+ applyTo: "**/*.cy.ts"
5
+ paths: ["**/*.cy.ts"]
6
+ ---
7
+
8
+ # Cypress Testing Guide
9
+
10
+ ## Test Types and Locations
11
+
12
+ - **E2E tests** — `cypress/e2e/` — run against a real or intercepted Cumulocity tenant
13
+ - **Component tests** — `cypress/component/` — mount Angular components in isolation via `cy.mount()`
14
+ - **Fixtures** — `cypress/fixtures/` — mocked BE responses for component tests
15
+ - **Support** — `cypress/support/` — custom commands (`cy.login()`, `cy.createDevice()`, etc.) and global configuration; auto-imported before every test
16
+ - **Snapshots** — `cypress/snapshots/` — baseline, actual, and diff images for visual regression tests (see Visual Testing)
17
+
18
+ ## E2E Tests
19
+
20
+ ### Authentication and navigation
21
+
22
+ ```typescript
23
+ beforeEach(() => {
24
+ cy.login(Cypress.env('username'), Cypress.env('password'));
25
+ });
26
+
27
+ cy.visit('/apps/cockpit/index.html#/group/123');
28
+ ```
29
+
30
+ Always use `Cypress.env('username')` / `Cypress.env('password')` — never hardcode credentials.
31
+
32
+ ### API interception
33
+
34
+ ```typescript
35
+ cy.intercept('GET', '/inventory/managedObjects/123', mockedObject).as('getMO');
36
+ cy.wait('@getMO');
37
+
38
+ // Query-parameter matching
39
+ cy.intercept(
40
+ { pathname: '/inventory/managedObjects', query: { pageSize: '5' } },
41
+ { managedObjects: [] }
42
+ );
43
+ ```
44
+
45
+ Use `cy.intercept()` to stub slow or unpredictable backend calls; never use fixed `cy.wait(number)`.
46
+
47
+ ### Creating and cleaning up test data
48
+
49
+ Use `cy.request()` for setup. Always clean up in `afterEach`:
50
+
51
+ ```typescript
52
+ afterEach(() => {
53
+ createdIds.forEach(id => {
54
+ cy.request({
55
+ url: `/inventory/managedObjects/${id}?cascade=true`,
56
+ method: 'DELETE',
57
+ failOnStatusCode: false
58
+ });
59
+ });
60
+ });
61
+ ```
62
+
63
+ Use `Cypress._.now()` to generate unique names: `e2eDevice${Cypress._.now()}`.
64
+
65
+ ## Component Tests
66
+
67
+ ### Mounting
68
+
69
+ ```typescript
70
+ cy.mount(MyComponent, {
71
+ imports: [CoreModule.forRoot(), CommonModule, ...],
72
+ providers: [
73
+ { provide: MyService, useValue: stubService }
74
+ ],
75
+ componentProperties: { myInput: value }
76
+ });
77
+ ```
78
+
79
+ All components should be standalone — import them directly, no NgModule wrapping needed.
80
+
81
+ ### Mocked backend with fixtures
82
+
83
+ Component tests that need BE responses can use recorded fixtures:
84
+
85
+ ```typescript
86
+ it('should load data', () => {
87
+ cy.intercept('GET', '/inventory/managedObjects/*', {
88
+ fixture: 'device.json'
89
+ }).as('getDevice');
90
+
91
+ cy.mount(MyComponent, { ... });
92
+ cy.wait('@getDevice');
93
+ cy.get('[data-cy="result"]').should('be.visible');
94
+ });
95
+ ```
96
+
97
+ Store fixture files in `cypress/fixtures/`.
98
+
99
+ ## Selectors
100
+
101
+ Prefer `data-cy` attributes, then role/title attributes. Avoid CSS class selectors unless the class is part of the component's public contract (e.g. icon class names from the design system).
102
+
103
+ Use consistent `data-cy` selectors with pattern: `<component-selector>--<element>-<details>`
104
+
105
+ - Double hyphens (`--`) separate component selector from element
106
+ - Single hyphens connect element type with descriptive details
107
+ - Don't hesitate to use longer names for precision
108
+
109
+ **Example:**
110
+ ```html
111
+ <!-- In component template -->
112
+ <button data-cy="c8y-custom-element-example--reset-button">Reset</button>
113
+ <button data-cy="c8y-custom-element-example--submit">Submit</button>
114
+ ```
115
+
116
+ **Usage:**
117
+ ```typescript
118
+ cy.get('[data-cy="c8y-custom-element-example--reset-button"]').click(); // preferred
119
+ cy.get('[title="Save"]').should('be.visible'); // acceptable for titled elements
120
+ cy.get('c8y-ui-empty-state').should('be.visible'); // OK for component element selectors
121
+ cy.get('.my-layout-class').should(...) // avoid unless class is stable/intentional
122
+ ```
123
+
124
+ ## Assertions
125
+
126
+ ```typescript
127
+ .should('be.visible')
128
+ .should('contain.text', 'expected')
129
+ .should('have.attr', 'href', '#/group/123')
130
+ .should('not.exist')
131
+ .should('be.disabled')
132
+ .should('be.enabled')
133
+ ```
134
+
135
+ Use `.should()` for all assertions (auto-retries). Avoid `.then()` for assertions.
136
+
137
+ ## Helper functions
138
+
139
+ Extract repeated selection and assertion logic into named helper functions at the top of the spec or in a describe-level scope:
140
+
141
+ ```typescript
142
+ function assertBreadcrumb(index: number, text: string) {
143
+ cy.get('[data-cy="breadcrumb-item"]').eq(index).should('contain.text', text);
144
+ }
145
+ ```
146
+
147
+ For component tests with multiple mounting configurations, create a named `mount*` function per variant.
148
+
149
+ ## Visual Testing
150
+
151
+ ```typescript
152
+ cy.compareSnapshot('my-identifier'); // strict
153
+ cy.compareSnapshot('my-identifier', 0.05); // with threshold
154
+ ```
155
+
156
+ Run component tests in the dev container for snapshot consistency with CI:
157
+
158
+ ```bash
159
+ yarn dc:up
160
+ yarn dc:run:spec "cypress/component/path/to/spec.cy.ts"
161
+ yarn dc:base:spec "cypress/component/path/to/spec.cy.ts" # regenerate baseline
162
+ ```
163
+
164
+ Snapshots: `cypress/snapshots/{base,actual,diff}/`
165
+
166
+ To regenerate baseline snapshots, use your project's snapshot regeneration command or manually replace files in `cypress/snapshots/base/`.
167
+
168
+ ## Running Tests
169
+
170
+ ```bash
171
+ # Open Cypress runner
172
+ npx cypress open --config baseUrl=https://<tenant>.cumulocity.com --env username=<u>,password=<p>
173
+
174
+ # Run headless
175
+ npx cypress run --config baseUrl=https://<tenant>.cumulocity.com --e2e --env username=<u>,password=<p> --browser chrome
176
+
177
+ # Filter by title substring (with cypress-grep plugin)
178
+ npx cypress run --env grep=breadcrumb
179
+ ```
180
+
181
+ ## What NOT to Do
182
+
183
+ - No `cy.wait(number)` — use `cy.wait('@alias')` or `.should()` retry
184
+ - No hardcoded tenant IDs, credentials, or device IDs — use `Cypress.env()` (except cases related to e.g. password changes; make sure to document these exceptions clearly in the code and not use credentials of real users)
185
+ - No `it.only` / `describe.only` / `context.only` committed to the repo
186
+ - Use `cy.visit()` for navigation; add custom wait utilities if needed
187
+ - Don't test trivial things just for coverage — test meaningful user-facing behavior
@@ -0,0 +1,251 @@
1
+ ---
2
+ name: 'Frontend Development Guide'
3
+ description: 'Instructions for Angular/TypeScript UI development'
4
+ ---
5
+
6
+ ## Persona
7
+
8
+ You are a senior Angular developer working on a **Cumulocity IoT application**. You leverage the latest Angular features: signals, standalone components, and new control flow syntax. Performance and consistency are paramount.
9
+
10
+ ---
11
+
12
+ ## Resources
13
+
14
+ - Angular docs: https://angular.dev/
15
+ - Angular style guide: https://angular.dev/style-guide
16
+ - Angular signals: https://angular.dev/guide/signals
17
+ - Cumulocity Web SDK / Codex: https://cumulocity.com/codex/
18
+ - Tutorial examples: https://github.com/Cumulocity-IoT/tutorial
19
+
20
+ ---
21
+
22
+ ## Before You Code
23
+
24
+ 1. **Check for existing patterns** — search the codebase for similar components or services before creating new ones. Check `@c8y/ngx-components` exports and [Codex](https://cumulocity.com/codex) (see main instructions for the full mandatory workflow).
25
+
26
+ ---
27
+
28
+ ## Critical Violations
29
+
30
+ These are the highest-severity issues — NEVER introduce them:
31
+
32
+ - **Unmanaged subscriptions** — every `.subscribe()` must have `takeUntilDestroyed()`, `takeUntil()`, or use `async` pipe
33
+ - **Empty catch blocks** — propagate, display via `AlertService.danger()`, or comment why swallowed
34
+ - **Committed secrets** — no API keys, tokens, passwords, or tenant-specific URLs in source
35
+ - **Dynamic gettext()** — `gettext()` must receive a static string literal; dynamic strings break the entire translation pipeline
36
+
37
+ ---
38
+
39
+ ## Components
40
+
41
+ - All new components must be **standalone** — do not set `standalone: true` explicitly (default since Angular 19+)
42
+ - Set `changeDetection: ChangeDetectionStrategy.OnPush` in all `@Component` decorators
43
+ - Keep components small — extract when template exceeds ~150 lines or class exceeds ~200 lines
44
+ - Components focus on presentation; extract business logic, data mapping, and SDK calls to dedicated services
45
+ - Use `readonly` for properties that should not change
46
+ - Use `input()` instead of `@Input`, `output()` instead of `@Output`, `viewChild()` instead of `@ViewChild`
47
+ - Do **not** use `@HostBinding` or `@HostListener` — use the `host` object in decorators
48
+ - Use `NgOptimizedImage` for static images (does not work for inline base64)
49
+ - Prefer Reactive forms over template-driven forms
50
+ - Define model interfaces and constants in **dedicated files** (`models.ts`, `types.ts`), not inside component files
51
+ - Keep JSDoc comments in sync with method signatures — out-of-sync documentation is worse than none
52
+ - Use strict type checking where available per package
53
+ - Split into `.ts`, `.html`, and `.scss` files. Use `c8y` prefix for component selectors
54
+
55
+ ### Example
56
+
57
+ ```ts
58
+ import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
59
+
60
+ @Component({
61
+ selector: 'c8y-example-component',
62
+ templateUrl: './example.component.html',
63
+ changeDetection: ChangeDetectionStrategy.OnPush
64
+ })
65
+ export class ExampleComponent {
66
+ protected readonly isServerRunning = signal(true);
67
+
68
+ toggleServerStatus() {
69
+ this.isServerRunning.update(v => !v);
70
+ }
71
+ }
72
+ ```
73
+
74
+ ```html
75
+ <section class="container">
76
+ @if (isServerRunning()) {
77
+ <span>{{ 'Yes, the server is running' | translate }}</span>
78
+ } @else {
79
+ <span>{{ 'No, the server is not running' | translate }}</span>
80
+ }
81
+ <button (click)="toggleServerStatus()">{{ 'Toggle Server Status' | translate }}</button>
82
+ </section>
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Templates
88
+
89
+ - Use **native control flow** (`@if`, `@for`, `@switch`) — `*ngIf`, `*ngFor`, `*ngSwitch` are **forbidden**
90
+ - Do **not** use `ngClass` or `ngStyle` — use `[class.x]` and `[style.x]` bindings
91
+ - Do **not** import `CoreModule` in standalone components
92
+ - Use `@let` to avoid repeating expressions
93
+ - Use `async` pipe for observables in templates
94
+ - `@for` track: use `item.id` or meaningful property; only `$index` for readonly primitives
95
+ - Keep templates simple — no method calls in bindings (except pipes), no complex logic
96
+ - Do not assume globals like `new Date()` are available in templates
97
+ - Import pipes explicitly when used in a template
98
+ - Use paths relative to the component `.ts` file for external templates/styles
99
+
100
+ ---
101
+
102
+ ## State Management & Subscriptions
103
+
104
+ - **Signals** for local synchronous state; `computed()` for derived state
105
+ - **RxJS/Promises** for async operations and API calls — don't mix paradigms in one use case
106
+ - Do **not** use `mutate` on signals — use `update` or `set`
107
+ - Do **not** run long-lived async (polling, intervals) in components — extract to services
108
+ - Always unsubscribe: `takeUntilDestroyed()`, `takeUntil()`, or `async` pipe — never bare `.unsubscribe()` without `ngOnDestroy`
109
+
110
+ ---
111
+
112
+ ## Dependency Injection & Services
113
+
114
+ - Use **one** DI pattern per file — don't mix constructor injection and `inject()` in the same file
115
+ - `providedIn: 'root'` only for global singletons — scope to components/features when possible
116
+ - Use `@c8y/client` for all Cumulocity REST calls — never `HttpClient` directly
117
+ - Lazy-load feature routes and C8Y widget/plugin modules
118
+
119
+ ---
120
+
121
+ ## Styling
122
+
123
+ - **Always use design tokens** — never hardcode colors (`#hex`, `rgb()`)
124
+ - `var(--brand-primary, var(--c8y-brand-primary))`
125
+ - Common tokens: `--c8y-brand-primary`, `--c8y-root-component-color-*`, `--c8y-root-component-background-*`, `--c8y-root-component-border-color`
126
+ - Prefer Cumulocity utility classes over custom CSS
127
+ - Avoid `::ng-deep` — use component encapsulation or design tokens
128
+ - Don't use inline styles
129
+ - Use SCSS for new files
130
+ - Reference: https://cumulocity.com/codex/design-system/design-tokens/overview
131
+
132
+ ### Utility Class Quick Reference
133
+
134
+ **Spacing:** `m-{side}-{amount}` / `p-{side}-{amount}` — sides: `t`, `r`, `b`, `l` or omit; amounts: 4, 8, 16, 24, 32, 40
135
+
136
+ **Layout:** `d-flex` (row), `d-col` (column) | `j-c-{start|center|end|between|around|evenly}` | `a-i-{start|center|end|stretch}` | `gap-{4|8|16}`
137
+
138
+ **Flex items:** `flex-grow`, `flex-no-shrink`, `flex-auto`, `fit-w`, `fit-h`, `min-width-0`, `min-height-0`
139
+
140
+ **Width/height:** `max-width-100`, `min-width-100`, `max-height-inherit`
141
+
142
+ **Position:** `p-relative`, `p-absolute`, `p-fixed`, `p-sticky`
143
+
144
+ **Text:** `text-left`, `text-center`, `text-right`, `text-pre-wrap`, `text-break-word`, `text-truncate`, `text-truncate-wrap`
145
+
146
+ **Display:** `d-flex`, `d-inline-flex`, `d-col`, `d-block`, `d-inline`, `d-inline-block`, `d-grid`, `d-contents`, `hidden`, `invisible`, `sr-only` | Responsive: `-xs`, `-sm`, `-md`, `-lg`
147
+
148
+ **Icons:** `[c8yIcon]="'icon-name'"` | sizes: `icon-16`, `icon-20`, `icon-32` | decorative: `aria-hidden="true"`
149
+
150
+ ---
151
+
152
+ ## Internationalization
153
+
154
+ - **Templates:** `{{ 'Text' | translate }}` for simple strings
155
+ - **Templates (conditional):** prefer `@let label = 'Text' | translate;` for conditional or repeated translations. `gettext()` in templates is also valid if exposed as a component property and piped through `| translate`
156
+ - **TypeScript:** `translateService.instant(gettext('Text'))` — `gettext()` marks for extraction only
157
+ - **Placeholders:** `{{ 'Result: {{count}}' | translate: { count: value } }}`
158
+ - Every user-visible string must be wrapped
159
+ - Reference: https://cumulocity.com/codex/components/application-and-system/internationalization/overview
160
+
161
+ ---
162
+
163
+ ## Error Handling
164
+
165
+ - **Never leave catch blocks empty** — propagate, display via `AlertService.danger()` with translations, or comment why swallowed
166
+ - API failures must be both logged to console AND displayed to the user
167
+
168
+ ---
169
+
170
+ ## Accessibility
171
+
172
+ WCAG 2.1 Level AA compliance is **mandatory** for all UI work.
173
+
174
+ ### Semantic HTML & Structure
175
+
176
+ - Use semantic elements (`<nav>`, `<main>`, `<section>`, `<article>`, `<aside>`, `<header>`, `<footer>`) — never `<div>` soup
177
+ - Heading levels (`<h1>`–`<h6>`) must follow a logical hierarchy — never skip levels for styling
178
+ - Use `<button>` for actions and `<a>` for navigation — never `<div (click)>` or `<span (click)>`
179
+ - Lists of items must use `<ul>`/`<ol>`/`<li>` — not styled divs
180
+ - Tables must have `<th>` with `scope` attributes; use `<caption>` for table purpose
181
+
182
+ ### Keyboard Operability (2.1.1, 2.1.2, 2.1.4)
183
+
184
+ - All interactive elements must be reachable and operable via keyboard alone
185
+ - Custom interactive elements need `tabindex="0"` and key event handlers (`Enter`, `Space`, `Escape`, arrow keys as appropriate)
186
+ - **No keyboard traps** — focus must always be escapable (modals must return focus to trigger on close)
187
+ - Manage focus programmatically on route changes and after dynamic content insertion
188
+ - Character key shortcuts (single letter) must be remappable, disableable, or only active on focus
189
+
190
+ ### Focus Management
191
+
192
+ - Focus order must follow a logical reading sequence (`tabindex` > 0 is **forbidden**)
193
+ - Focus must be visible at all times — never `outline: none` without a visible replacement
194
+ - Trap focus inside modals/dialogs while open; restore focus to trigger element on close
195
+ - Use `cdkTrapFocus` or `cdkFocusInitial` from `@angular/cdk/a11y` for focus trapping
196
+ - After dynamic content changes (route navigation, drawer open/close), move focus to the new content
197
+
198
+ ### Color & Contrast (1.4.1, 1.4.3, 1.4.11)
199
+
200
+ - **Text contrast:** minimum 4.5:1 for normal text, 3:1 for large text (18px+ or 14px+ bold)
201
+ - **Non-text contrast:** UI components and graphical objects need minimum 3:1 contrast against adjacent colors
202
+ - **Never use color alone** to convey information — always pair with text, icons, or patterns (e.g., error states need icon + color + text, not just red)
203
+ - Use design tokens exclusively — they are pre-validated for contrast compliance, never add custom colors
204
+
205
+ ### Forms & Input (1.3.5, 3.3.1, 3.3.2, 3.3.3, 3.3.4)
206
+
207
+ - Every form control must have a visible `<label>` associated via `for`/`id` — never placeholder-only labels
208
+ - Use `autocomplete` attributes on fields collecting personal data (`name`, `email`, `tel`, `street-address`, etc.)
209
+ - Error messages must: identify the field in error, describe the error, and suggest correction
210
+ - Display errors with `role="alert"` or `aria-live="assertive"` so screen readers announce them
211
+ - Group related controls with `<fieldset>` and `<legend>`
212
+ - Required fields must be marked with `aria-required="true"` and a visible indicator
213
+
214
+ ### Images & Icons (1.1.1)
215
+
216
+ - Informative images: `alt` text describing content or function
217
+ - Decorative images/icons: `aria-hidden="true"` and empty `alt=""`
218
+ - Icon-only buttons must have `aria-label` or visually hidden text: `<button aria-label="{{ 'Delete item' | translate }}"><i [c8yIcon]="'minus-circle'" aria-hidden="true"></i></button>`
219
+ - Complex images (charts, diagrams): provide text alternative via `aria-describedby` pointing to a description
220
+
221
+ ### Dynamic Content & Live Regions (4.1.3)
222
+
223
+ - Status messages (success, info, warnings) must use `role="status"` or `aria-live="polite"`
224
+ - Urgent messages (errors, alerts) must use `role="alert"` or `aria-live="assertive"`
225
+ - Loading states: announce start and end — e.g., `aria-busy="true"` on the container, announce completion
226
+ - Content that updates without page reload must notify assistive technology
227
+ - Never auto-update content faster than the user can read it; provide pause/stop controls for auto-rotating content
228
+
229
+ ### Text & Content (1.4.4, 1.4.10, 1.4.12, 1.4.13)
230
+
231
+ - Text must be resizable up to 200% without loss of content or functionality
232
+ - Content must reflow at 320px viewport width without horizontal scrolling
233
+ - No loss of content when users override text spacing (line height 1.5x, letter spacing 0.12em, word spacing 0.16em)
234
+ - Content revealed on hover/focus (tooltips) must be: dismissible (Esc), hoverable (mouse can reach it), and persistent (stays until dismissed)
235
+
236
+ ### ARIA Usage
237
+
238
+ - **First rule of ARIA:** don't use ARIA if a native HTML element provides the semantics (`<button>` over `<div role="button">`)
239
+ - Use `aria-label` or `aria-labelledby` for elements without visible text labels
240
+ - Use `aria-describedby` for supplementary descriptions (help text, constraints)
241
+ - Use `aria-expanded`, `aria-controls`, `aria-haspopup` for disclosure widgets
242
+ - Custom widgets (tabs, trees, comboboxes) must implement the full [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) keyboard and role pattern
243
+ - Use `aria-current="page"` for active navigation links
244
+
245
+ ### Testing Checklist
246
+
247
+ - **Keyboard-only:** tab through entire page, operate every control, escape every modal
248
+ - **Screen reader:** test critical flows with VoiceOver (macOS) — ensure all content is announced
249
+ - **Zoom:** verify layout at 200% zoom and 320px viewport
250
+ - **Color:** inspect with simulated color-blindness (DevTools → Rendering → Emulate vision deficiencies)
251
+ - **Automated:** run `axe-core` or Lighthouse accessibility audit — zero violations is the baseline, not the goal