weg-shared-layout 0.0.31 → 0.0.33

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 (31) hide show
  1. package/LICENSE +21 -21
  2. package/dist/cjs/weg-footer_2.cjs.entry.js +6 -6
  3. package/dist/collection/components/weg-footer/icons/instagram.svg +5 -5
  4. package/dist/collection/components/weg-footer/icons/linkedin.svg +3 -3
  5. package/dist/collection/components/weg-footer/icons/tiktok.svg +3 -3
  6. package/dist/collection/components/weg-footer/icons/youtube.svg +4 -4
  7. package/dist/collection/components/weg-footer/weg-footer.css +263 -263
  8. package/dist/collection/components/weg-footer/weg-footer.js +2 -2
  9. package/dist/collection/components/weg-header/weg-header.css +483 -483
  10. package/dist/collection/components/weg-header/weg-header.js +4 -4
  11. package/dist/collection/styles/shared.css +103 -103
  12. package/dist/components/weg-footer.js +1 -1
  13. package/dist/components/weg-header.js +1 -1
  14. package/dist/esm/weg-footer_2.entry.js +6 -6
  15. package/dist/weg-shared-layout/p-b38f951e.entry.js +1 -0
  16. package/dist/weg-shared-layout/weg-shared-layout.esm.js +1 -1
  17. package/docs/README.md +49 -51
  18. package/docs/angular.md +402 -258
  19. package/docs/nextjs.md +314 -314
  20. package/docs/publishing.md +65 -65
  21. package/docs/react.md +272 -272
  22. package/docs/vanilla.md +139 -139
  23. package/package.json +81 -81
  24. package/readme.md +95 -97
  25. package/src/constants/breakpoints.ts +7 -7
  26. package/src/styles/container.css +60 -60
  27. package/dist/weg-shared-layout/p-37f65fe1.entry.js +0 -1
  28. package/docs/angular-16-compatibility.md +0 -250
  29. package/docs/angular-integration-guide.md +0 -1035
  30. /package/dist/types/Users/{rakin/Documents/WEG/repos → tan/Documents/code}/weg-shared-layout/.stencil/test-utils/axe.d.ts +0 -0
  31. /package/dist/types/Users/{rakin/Documents/WEG/repos → tan/Documents/code}/weg-shared-layout/.stencil/test-utils/viewport.d.ts +0 -0
package/docs/angular.md CHANGED
@@ -1,258 +1,402 @@
1
- # Angular
2
-
3
- Short quick start for Angular 16+. For the full **step-by-step** guide (HTTP, API mapping, testing, SSR, troubleshooting), see **[angular-integration-guide.md](./angular-integration-guide.md)**.
4
-
5
- | Demo | When |
6
- | --- | --- |
7
- | **[demo/angular16/README.md](../demo/angular16/README.md)** | Angular **16** — same imports as `npm i` (in-repo: `file:../..`) |
8
- | [weg-angular-demo](https://github.com/jobsac/weg-angular-demo) | Newer Angular (signals) |
9
-
10
- ## Integration checklist
11
-
12
- - [ ] `npm i weg-shared-layout`
13
- - [ ] `defineCustomElements()` in `main.ts` **before** bootstrap
14
- - [ ] `skipLibCheck`, `resolveJsonModule`, `moduleResolution: "bundler"` (or `node16`) — see [Step 3 in integration guide](./angular-integration-guide.md#step-3--typescript-import-layout-json)
15
- - [ ] `auth.ts` with `HEADER_SIGN_IN` / `ACCOUNT_LOGIN_HREF` (host app constants)
16
- - [ ] `CUSTOM_ELEMENTS_SCHEMA` on the shell component that uses the tags
17
- - [ ] `[layout]="layoutObject"` on `<weg-header>` and `<weg-footer>`
18
- - [ ] Header: `[signedIn]`, `[userName]`, `(wegAuthClick)` when using auth
19
-
20
- ## Components
21
-
22
- | Tag | Purpose |
23
- | --- | --- |
24
- | `<weg-header>` | Header — CMS nav (signed out), built-in nav (signed in), auth |
25
- | `<weg-footer>` | Footer — social, menu, credits, copyright |
26
-
27
- Both accept the same `layout` payload ([`dummy-data.json`](../src/assets/dummy-data.json) or your API). `<weg-header>` also accepts **`signed-in`**, **`user-name`**, and emits **`wegAuthClick`**.
28
-
29
- ## 1. Install
30
-
31
- ```bash
32
- npm i weg-shared-layout
33
- ```
34
-
35
- ## 2. Register custom elements (once, before bootstrap)
36
-
37
- **Recommended** — loader in `src/main.ts`:
38
-
39
- ```ts
40
- import { bootstrapApplication } from '@angular/platform-browser';
41
- import { defineCustomElements } from 'weg-shared-layout/loader';
42
-
43
- import { appConfig } from './app/app.config';
44
- import { App } from './app/app';
45
-
46
- defineCustomElements();
47
-
48
- bootstrapApplication(App, appConfig).catch((err) => console.error(err));
49
- ```
50
-
51
- **Alternative** — side-effect imports (one of these approaches, not both; needs `moduleResolution` `bundler` / `node16`):
52
-
53
- ```ts
54
- import 'weg-shared-layout/weg-header';
55
- import 'weg-shared-layout/weg-footer';
56
- ```
57
-
58
- ## 3. Layout types + TypeScript
59
-
60
- ```ts
61
- // src/app/layout.types.ts
62
- import layoutFixture from 'weg-shared-layout/dummy-data.json';
63
-
64
- export type LayoutData = typeof layoutFixture;
65
- ```
66
-
67
- ```json
68
- // tsconfig.json (or tsconfig.app.json) — required for JSON + package subpaths
69
- {
70
- "compilerOptions": {
71
- "skipLibCheck": true,
72
- "resolveJsonModule": true,
73
- "moduleResolution": "bundler"
74
- },
75
- "include": ["src/**/*.ts"]
76
- }
77
- ```
78
-
79
- Angular 16 pitfalls (Stencil `.d.ts`, classic `node` resolution): **[angular-16-compatibility.md](./angular-16-compatibility.md)**.
80
-
81
- ## 4. Allow custom elements in templates
82
-
83
- Add `schemas: [CUSTOM_ELEMENTS_SCHEMA]` to every `@Component` (or `@NgModule`) whose template uses `<weg-header>` or `<weg-footer>`. Does **not** cascade to `router-outlet` children.
84
-
85
- ## 5. Auth constants
86
-
87
- ```ts
88
- // src/app/auth.ts
89
- export const HEADER_SIGN_IN = {
90
- label: 'Sign in',
91
- href: 'https://account.warwickemploymentgroup.com/account/login',
92
- };
93
-
94
- export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
95
- ```
96
-
97
- ## 6. Layout shell — pick one
98
-
99
- Use **`[layout]="..."`** so Angular sets the JavaScript `layout` property, not an HTML attribute.
100
-
101
- | Variant | Angular | Template bindings |
102
- | --- | --- | --- |
103
- | **6A** Plain properties | 16+ | `[layout]="layoutData"` |
104
- | **6B** Signals | 16.1+ | `[layout]="layoutData()"` |
105
-
106
- Full HTTP examples: integration guide **[Step 7A / 7B](./angular-integration-guide.md#step-7--load-layout-from-your-api)**.
107
-
108
- ### 6A — Plain properties (Angular 16+)
109
-
110
- ```ts
111
- // src/app/app.ts
112
- import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
113
- import { RouterOutlet } from '@angular/router';
114
- import layoutFixture from 'weg-shared-layout/dummy-data.json';
115
-
116
- import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
117
-
118
- @Component({
119
- selector: 'app-root',
120
- imports: [RouterOutlet],
121
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
122
- templateUrl: './app.html',
123
- })
124
- export class App {
125
- layoutData = layoutFixture;
126
-
127
- signedIn = false;
128
- userName?: string;
129
-
130
- onAuthClick(event: Event): void {
131
- const customEvent = event as CustomEvent<{ action: 'sign-in' | 'sign-out' }>;
132
- customEvent.preventDefault();
133
-
134
- if (customEvent.detail.action === 'sign-out') {
135
- this.signedIn = false;
136
- window.location.href = ACCOUNT_LOGIN_HREF;
137
- return;
138
- }
139
-
140
- window.location.href = HEADER_SIGN_IN.href;
141
- }
142
- }
143
- ```
144
-
145
- ```html
146
- <!-- src/app/app.html -->
147
- <weg-header
148
- [layout]="layoutData"
149
- [signedIn]="signedIn"
150
- [userName]="userName"
151
- (wegAuthClick)="onAuthClick($event)"
152
- ></weg-header>
153
-
154
- <router-outlet />
155
-
156
- <weg-footer [layout]="layoutData"></weg-footer>
157
- ```
158
-
159
- In production, load the same shape from your API — see **[Step 7A](./angular-integration-guide.md#step-7a--http-with-rxjs--async-pipe-pairs-with-6a)**. If your API returns separate arrays, see **[mapping section](./angular-integration-guide.md#optional-api-returns-separate-arrays-not-one-layout-object)**.
160
-
161
- ### 6B — Signals (Angular 16.1+)
162
-
163
- ```ts
164
- // src/app/app.ts
165
- import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';
166
- import { RouterOutlet } from '@angular/router';
167
- import layoutFixture from 'weg-shared-layout/dummy-data.json';
168
-
169
- import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
170
- import type { LayoutData } from './layout.types';
171
-
172
- @Component({
173
- selector: 'app-root',
174
- imports: [RouterOutlet],
175
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
176
- templateUrl: './app.html',
177
- })
178
- export class App {
179
- readonly layoutData = signal<LayoutData>(layoutFixture);
180
-
181
- readonly signedIn = signal(false);
182
- readonly userName = signal<string | undefined>(undefined);
183
-
184
- onAuthClick(event: Event): void {
185
- const customEvent = event as CustomEvent<{ action: 'sign-in' | 'sign-out' }>;
186
- customEvent.preventDefault();
187
-
188
- if (customEvent.detail.action === 'sign-out') {
189
- this.signedIn.set(false);
190
- this.userName.set(undefined);
191
- window.location.href = ACCOUNT_LOGIN_HREF;
192
- return;
193
- }
194
-
195
- window.location.href = HEADER_SIGN_IN.href;
196
- }
197
- }
198
- ```
199
-
200
- ```html
201
- <!-- src/app/app.html -->
202
- <weg-header
203
- [layout]="layoutData()"
204
- [signedIn]="signedIn()"
205
- [userName]="userName()"
206
- (wegAuthClick)="onAuthClick($event)"
207
- ></weg-header>
208
-
209
- <router-outlet />
210
-
211
- <weg-footer [layout]="layoutData()"></weg-footer>
212
- ```
213
-
214
- HTTP with signals: **[Step 7B](./angular-integration-guide.md#step-7b--http-with-signals--tosignal-pairs-with-6b)** (`toSignal` + same `LayoutService`).
215
-
216
- ## Header: `signed-in`, `user-name`, `wegAuthClick`
217
-
218
- | Input / output | Binding (6A) | Binding (6B) | Notes |
219
- | --- | --- | --- | --- |
220
- | CMS nav | `[layout]="layoutData"` | `[layout]="layoutData()"` | `header.menu` — groups (`items`) and flat links (`href`) when signed out |
221
- | Session state | `[signedIn]="signedIn"` | `[signedIn]="signedIn()"` | `true` → built-in signed-in nav |
222
- | User name | `[userName]="userName"` | `[userName]="userName()"` | Manage Account label when signed in |
223
- | Auth click | `(wegAuthClick)="onAuthClick($event)"` | `detail.action`: `'sign-in'` \| `'sign-out'` |
224
- | Prevent default | `preventDefault()` in handler | Stops component default navigation |
225
-
226
- **Signed-in nav (built in):** Find a job, Dashboard, Manage Account, Sign out.
227
-
228
- Logo **image** uses `layout.header.logoSrc` (bundled if omitted). Logo **link** always goes to WEG home.
229
-
230
- ## Footer
231
-
232
- `<weg-footer>` only needs `[layout]`. See [`dummy-data.json`](../src/assets/dummy-data.json) for `footer` shape.
233
-
234
- ## Troubleshooting
235
-
236
- | Symptom | Cause / fix |
237
- | --- | --- |
238
- | `'weg-header'` / `'weg-footer'` is not a known element | `CUSTOM_ELEMENTS_SCHEMA` on the template's component |
239
- | Header/footer empty | `defineCustomElements()` before bootstrap, or missing `[layout]` |
240
- | Nav empty after passing an array | `[layout]` must be `{ header, footer }`, not a bare array |
241
- | Auth always Sign in | `[signedIn]` not bound or still `false` |
242
- | Manage Account generic | `[userName]` not set when signed in |
243
- | SSR: `document is not defined` | Guard `defineCustomElements()` with `typeof window !== 'undefined'` |
244
- | `stencil-public-runtime.d.ts` / `Mixin<const …>` errors | TypeScript 4.9 vs Stencil 5 `.d.ts` | **[angular-16-compatibility.md](./angular-16-compatibility.md)** |
245
- | Cannot resolve `weg-shared-layout/weg-header` | Old `moduleResolution` | Loader in `main.ts` or `bundler` / `node16` — **[angular-16-compatibility.md](./angular-16-compatibility.md)** |
246
-
247
- More detail: **[integration guide § Troubleshooting](./angular-integration-guide.md#troubleshooting)**.
248
-
249
- ## See also
250
-
251
- - **[Documentation index](./README.md)**
252
- - **[In-repo Angular 16 demo](../demo/angular16/README.md)**
253
- - **[Angular 16 / TS 4.9 compatibility](./angular-16-compatibility.md)** — `stencil-public-runtime` errors, subpath imports
254
- - **[Angular integration guide (step-by-step)](./angular-integration-guide.md)**
255
- - **[React SPA](./react.md)**
256
- - **[Next.js App Router](./nextjs.md)**
257
- - **[Plain HTML / vanilla JS](./vanilla.md)**
258
- - **[Package readme](../readme.md)**
1
+ # Angular 16 integration
2
+
3
+ Step-by-step guide for **Angular 16** (standalone or NgModule). Uses plain component properties — no signals.
4
+
5
+ Runnable reference: **[demo/angular16](../demo/angular16/README.md)** (`npm run demo:angular16` from repo root).
6
+
7
+ Newer Angular (signals): [weg-angular-demo](https://github.com/jobsac/weg-angular-demo) — same layout ideas, different syntax.
8
+
9
+ ---
10
+
11
+ ## Checklist
12
+
13
+ Copy this while integrating:
14
+
15
+ - [ ] **Step 1** — `npm i weg-shared-layout`
16
+ - [ ] **Step 2** — `tsconfig`: `skipLibCheck`, `resolveJsonModule`, `moduleResolution`
17
+ - [ ] **Step 3** — `defineCustomElements()` in `main.ts` **before** bootstrap
18
+ - [ ] **Step 4** — `layout.types.ts` from package fixture
19
+ - [ ] **Step 5** — `auth.ts` with sign-in URL constants (your app, not the package)
20
+ - [ ] **Step 6** — `CUSTOM_ELEMENTS_SCHEMA` on the shell component
21
+ - [ ] **Step 7** — Template with **`[layout]`** property binding
22
+ - [ ] **Step 8** — Header: `[signedIn]`, `[userName]`, `(wegAuthClick)`
23
+ - [ ] **Step 9** — Verify in DevTools: `$0.layout` is an object on `<weg-header>`
24
+
25
+ ---
26
+
27
+ ## Step 1 — Install
28
+
29
+ ```bash
30
+ npm i weg-shared-layout
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Step 2 — TypeScript config
36
+
37
+ In **`tsconfig.json`** and/or **`tsconfig.app.json`**:
38
+
39
+ ```json
40
+ {
41
+ "compilerOptions": {
42
+ "skipLibCheck": true,
43
+ "resolveJsonModule": true,
44
+ "moduleResolution": "bundler"
45
+ },
46
+ "include": ["src/**/*.ts"]
47
+ }
48
+ ```
49
+
50
+ | Option | Why |
51
+ | --- | --- |
52
+ | `skipLibCheck` | Skips Stencil `stencil-public-runtime.d.ts` (TS 5 syntax) when on TS 4.9 |
53
+ | `resolveJsonModule` | Allows `import … from 'weg-shared-layout/dummy-data.json'` |
54
+ | `moduleResolution: "bundler"` | Resolves package `exports` (`loader`, `dummy-data.json`) |
55
+
56
+ Use **`node16`** instead of **`bundler`** if you must stay on TypeScript 4.9. Classic **`node`** does not resolve subpaths reliably.
57
+
58
+ ---
59
+
60
+ ## Step 3 — Register custom elements
61
+
62
+ Call the Stencil loader in **`src/main.ts`** once, **before** bootstrap:
63
+
64
+ ```ts
65
+ import { bootstrapApplication } from '@angular/platform-browser';
66
+ import { defineCustomElements } from 'weg-shared-layout/loader';
67
+
68
+ import { AppComponent } from './app/app.component';
69
+ import { appConfig } from './app/app.config';
70
+
71
+ defineCustomElements();
72
+
73
+ bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
74
+ ```
75
+
76
+ If header/footer tags appear but nav is empty, registration did not run or ran too late.
77
+
78
+ **NgModule apps:** same `defineCustomElements()` call, then `platformBrowserDynamic().bootstrapModule(AppModule)`.
79
+
80
+ ---
81
+
82
+ ## Step 4 — Layout types
83
+
84
+ ```ts
85
+ // src/app/layout.types.ts
86
+ import layoutFixture from 'weg-shared-layout/dummy-data.json';
87
+
88
+ export type LayoutData = typeof layoutFixture;
89
+ ```
90
+
91
+ Alternative (types only, no JSON import):
92
+
93
+ ```ts
94
+ import type { LayoutData } from 'weg-shared-layout/layout-data';
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Step 5 — Auth URLs
100
+
101
+ Host apps own sign-in/out URLs — do not import these from the package.
102
+
103
+ ```ts
104
+ // src/app/auth.ts
105
+ export const HEADER_SIGN_IN = {
106
+ label: 'Sign in',
107
+ href: 'https://account.warwickemploymentgroup.com/account/login',
108
+ };
109
+
110
+ export const ACCOUNT_LOGIN_HREF = HEADER_SIGN_IN.href;
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Step 6 — Shell component
116
+
117
+ Add `CUSTOM_ELEMENTS_SCHEMA` to the component whose template contains `<weg-header>` / `<weg-footer>`. It does **not** cascade to routed children.
118
+
119
+ ```ts
120
+ // src/app/app.component.ts
121
+ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
122
+ import { RouterOutlet } from '@angular/router';
123
+ import layoutFixture from 'weg-shared-layout/dummy-data.json';
124
+
125
+ import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
126
+ import type { LayoutData } from './layout.types';
127
+
128
+ @Component({
129
+ selector: 'app-root',
130
+ standalone: true,
131
+ imports: [RouterOutlet],
132
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
133
+ templateUrl: './app.component.html',
134
+ })
135
+ export class AppComponent {
136
+ layoutData: LayoutData = layoutFixture;
137
+
138
+ signedIn = false;
139
+ userName?: string;
140
+
141
+ onAuthClick(event: Event): void {
142
+ const customEvent = event as CustomEvent<{ action: 'sign-in' | 'sign-out' }>;
143
+ customEvent.preventDefault();
144
+
145
+ if (customEvent.detail.action === 'sign-out') {
146
+ this.signedIn = false;
147
+ window.location.href = ACCOUNT_LOGIN_HREF;
148
+ return;
149
+ }
150
+
151
+ window.location.href = HEADER_SIGN_IN.href;
152
+ }
153
+ }
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Step 7 — Template
159
+
160
+ Use **`[layout]="..."`** so Angular sets the JavaScript property, not an HTML attribute.
161
+
162
+ ```html
163
+ <!-- src/app/app.component.html -->
164
+ <weg-header
165
+ [layout]="layoutData"
166
+ [signedIn]="signedIn"
167
+ [userName]="userName"
168
+ (wegAuthClick)="onAuthClick($event)"
169
+ ></weg-header>
170
+
171
+ <main>
172
+ <router-outlet></router-outlet>
173
+ </main>
174
+
175
+ <weg-footer [layout]="layoutData"></weg-footer>
176
+ ```
177
+
178
+ | Binding | Purpose |
179
+ | --- | --- |
180
+ | `[layout]` | CMS nav when signed out; same object for header and footer |
181
+ | `[signedIn]` | `true` → built-in signed-in nav (ignores CMS `header.menu`) |
182
+ | `[userName]` | First name on Manage Account when signed in |
183
+ | `(wegAuthClick)` | `detail.action`: `'sign-in'` \| `'sign-out'` — call `preventDefault()` when you handle navigation |
184
+
185
+ Run the app — you should see header nav and footer.
186
+
187
+ ---
188
+
189
+ ## Step 8 — Load layout from your API (optional)
190
+
191
+ When ready, replace the static fixture with HTTP. Register `provideHttpClient()` in `app.config.ts`.
192
+
193
+ **`src/app/layout.service.ts`:**
194
+
195
+ ```ts
196
+ import { Injectable } from '@angular/core';
197
+ import { HttpClient } from '@angular/common/http';
198
+ import { map, Observable } from 'rxjs';
199
+
200
+ import { HEADER_SIGN_IN } from './auth';
201
+ import type { LayoutData } from './layout.types';
202
+
203
+ @Injectable({ providedIn: 'root' })
204
+ export class LayoutService {
205
+ constructor(private http: HttpClient) {}
206
+
207
+ loadLayout(): Observable<LayoutData> {
208
+ return this.http.get<LayoutData>('/api/layout').pipe(
209
+ map((data) => this.ensureSignInMenu(data)),
210
+ );
211
+ }
212
+
213
+ private ensureSignInMenu(data: LayoutData): LayoutData {
214
+ const menu = data.header?.menu ?? [];
215
+ const hasSignIn = menu.some(
216
+ (item) =>
217
+ !item.items?.length &&
218
+ item.label?.trim().toLowerCase() === 'sign in',
219
+ );
220
+ if (hasSignIn) return data;
221
+ return {
222
+ ...data,
223
+ header: { ...data.header, menu: [...menu, HEADER_SIGN_IN] },
224
+ };
225
+ }
226
+ }
227
+ ```
228
+
229
+ **`src/app/app.component.ts`** (HTTP variant):
230
+
231
+ ```ts
232
+ import { AsyncPipe } from '@angular/common';
233
+ import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
234
+ import { RouterOutlet } from '@angular/router';
235
+ import { Observable } from 'rxjs';
236
+
237
+ import { ACCOUNT_LOGIN_HREF, HEADER_SIGN_IN } from './auth';
238
+ import type { LayoutData } from './layout.types';
239
+ import { LayoutService } from './layout.service';
240
+
241
+ @Component({
242
+ selector: 'app-root',
243
+ standalone: true,
244
+ imports: [AsyncPipe, RouterOutlet],
245
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
246
+ templateUrl: './app.component.html',
247
+ })
248
+ export class AppComponent {
249
+ layout$: Observable<LayoutData>;
250
+ signedIn = false;
251
+ userName?: string;
252
+
253
+ constructor(layoutService: LayoutService) {
254
+ this.layout$ = layoutService.loadLayout();
255
+ }
256
+
257
+ onAuthClick(event: Event): void {
258
+ const customEvent = event as CustomEvent<{ action: 'sign-in' | 'sign-out' }>;
259
+ customEvent.preventDefault();
260
+
261
+ if (customEvent.detail.action === 'sign-out') {
262
+ this.signedIn = false;
263
+ window.location.href = ACCOUNT_LOGIN_HREF;
264
+ return;
265
+ }
266
+
267
+ window.location.href = HEADER_SIGN_IN.href;
268
+ }
269
+ }
270
+ ```
271
+
272
+ **`src/app/app.component.html`** (Angular 16 — use `*ngIf`, not `@if`):
273
+
274
+ ```html
275
+ <ng-container *ngIf="layout$ | async as layout">
276
+ <weg-header
277
+ [layout]="layout"
278
+ [signedIn]="signedIn"
279
+ [userName]="userName"
280
+ (wegAuthClick)="onAuthClick($event)"
281
+ ></weg-header>
282
+
283
+ <main>
284
+ <router-outlet></router-outlet>
285
+ </main>
286
+
287
+ <weg-footer [layout]="layout"></weg-footer>
288
+ </ng-container>
289
+ ```
290
+
291
+ Add `CommonModule` or `NgIf` + `AsyncPipe` to `imports` if not already present.
292
+
293
+ ---
294
+
295
+ ## Step 9 — Verify
296
+
297
+ 1. Open DevTools → Elements → select `<weg-header>`.
298
+ 2. In the console:
299
+
300
+ ```js
301
+ $0.layout // must be a plain object, not undefined
302
+ $0.signedIn // boolean
303
+ $0.userName // string or undefined
304
+ ```
305
+
306
+ If `layout` looks like `"[object Object]"`, you used attribute binding instead of `[layout]`.
307
+
308
+ ---
309
+
310
+ ## Layout shape
311
+
312
+ Both tags accept one **`layout`** object with `header` and `footer` keys — **not** a bare array at the top level.
313
+
314
+ Full reference: [`dummy-data.json`](../src/assets/dummy-data.json) or [`GET /api/layout`](https://weg-payload-test.vercel.app/api/layout).
315
+
316
+ ```json
317
+ {
318
+ "header": {
319
+ "menu": [
320
+ { "label": "Find a job", "items": [{ "label": "Graduates", "href": "…" }] },
321
+ { "label": "Career advice", "href": "…" },
322
+ { "label": "Sign in", "href": "…" }
323
+ ]
324
+ },
325
+ "footer": {
326
+ "menu": [[{ "label": "About WEG", "href": "…" }]],
327
+ "social": [{ "platform": "LinkedIn", "href": "…" }],
328
+ "credits": "…",
329
+ "copyright": "…"
330
+ }
331
+ }
332
+ ```
333
+
334
+ ### API returns separate arrays?
335
+
336
+ If your API does not return one `{ header, footer }` object, merge in a service:
337
+
338
+ ```ts
339
+ forkJoin({
340
+ menuGroups: this.http.get<LayoutData['header']>('/api/nav/menu'),
341
+ flatLinks: this.http.get<LayoutData['header']>('/api/nav/links'),
342
+ footer: this.http.get<LayoutData['footer']>('/api/footer'),
343
+ }).pipe(
344
+ map(({ menuGroups, flatLinks, footer }) => ({
345
+ header: { menu: [...menuGroups, ...flatLinks, HEADER_SIGN_IN] },
346
+ footer,
347
+ })),
348
+ );
349
+ ```
350
+
351
+ ---
352
+
353
+ ## Troubleshooting
354
+
355
+ | Symptom | Fix |
356
+ | --- | --- |
357
+ | `'weg-header'` is not a known element | `CUSTOM_ELEMENTS_SCHEMA` on the component that owns the template |
358
+ | Header/footer empty | `defineCustomElements()` before bootstrap |
359
+ | Nav empty after passing data | `[layout]` must be `{ header, footer }`, not a bare array |
360
+ | Auth always Sign in | Bind `[signedIn]` from session state |
361
+ | Generic Manage Account | Pass `[userName]` when signed in |
362
+ | `Mixin<const …>` / TS1139 in `stencil-public-runtime.d.ts` | Add `skipLibCheck: true` and/or upgrade to TypeScript 5.0+ |
363
+ | Cannot resolve `weg-shared-layout/loader` | Set `moduleResolution` to `bundler` or `node16` |
364
+ | SSR: `document is not defined` | Guard: `if (typeof window !== 'undefined') defineCustomElements()` |
365
+
366
+ ### TypeScript 4.9 + Stencil errors
367
+
368
+ Stencil ships `.d.ts` files with TypeScript 5 syntax. On Angular 16 with TS 4.9:
369
+
370
+ 1. Add **`skipLibCheck: true`** (recommended), or
371
+ 2. Upgrade to **TypeScript 5.0–5.1** (supported by Angular 16.1+): `npm i -D typescript@~5.1.6`
372
+
373
+ For typing only, avoid pulling Stencil runtime declarations:
374
+
375
+ ```ts
376
+ import type { LayoutData } from 'weg-shared-layout/layout-data';
377
+ // or
378
+ export type LayoutData = typeof layoutFixture; // from dummy-data.json
379
+ ```
380
+
381
+ ---
382
+
383
+ ## File map
384
+
385
+ | File | Role |
386
+ | --- | --- |
387
+ | `src/main.ts` | `defineCustomElements()` then bootstrap |
388
+ | `tsconfig.json` | `skipLibCheck`, `resolveJsonModule`, `moduleResolution` |
389
+ | `src/app/layout.types.ts` | `export type LayoutData = typeof layoutFixture` |
390
+ | `src/app/auth.ts` | `HEADER_SIGN_IN`, `ACCOUNT_LOGIN_HREF` |
391
+ | `src/app/layout.service.ts` | HTTP / API mapping (optional) |
392
+ | `src/app/app.component.ts` | Shell: schema, layout, auth handler |
393
+ | `src/app/app.component.html` | `<weg-header>`, `<router-outlet>`, `<weg-footer>` |
394
+
395
+ ---
396
+
397
+ ## See also
398
+
399
+ - [Documentation index](./README.md)
400
+ - [In-repo Angular 16 demo](../demo/angular16/README.md)
401
+ - [Package readme](../readme.md)
402
+ - [React SPA](./react.md) · [Next.js](./nextjs.md) · [Vanilla JS](./vanilla.md)