@startnext/chrome 0.3.2 → 0.3.3

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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Startnext Header/Footer Web Components
4
4
 
5
- Wiederverwendbare Header- und Footer-Components für alle Startnext Microservices.
5
+ Wiederverwendbare Header- und Footer-Components fuer alle Startnext Microservices.
6
6
 
7
7
  ## Installation
8
8
 
@@ -17,7 +17,7 @@ pnpm add @startnext/chrome
17
17
  Es gibt zwei Integrationsmodi:
18
18
 
19
19
  1. **Client-Side Rendering** — Web Component fetcht Daten selbst (einfach, kein Server noetig)
20
- 2. **Server-Side Rendering** — Server holt fertig gerendertes HTML von der API (sofort sichtbar ohne JS, bessere Performance + SEO)
20
+ 2. **Server-Side Rendering (SSR)** — Server holt fertig gerendertes HTML von der API (sofort sichtbar ohne JS, bessere Performance + SEO)
21
21
 
22
22
  ### Client-Side: Via CDN (PHP, Vanilla HTML)
23
23
  ```html
@@ -39,7 +39,7 @@ import '@startnext/chrome';
39
39
 
40
40
  ### Server-Side Rendering (empfohlen)
41
41
 
42
- SSR-HTML von der API holen und direkt einbinden. Die Web Component JS hydriert anschliessend (haengt Events an, statt DOM neu zu rendern). Siehe Abschnitt [SSR mit React / Next.js](#ssr-mit-react--nextjs-app-router) oder [SSR PHP / ESI](#ssr--php-beispiel) weiter unten.
42
+ SSR-HTML von der API holen und direkt einbinden. Die Web Component JS hydriert anschliessend (haengt Events an, statt DOM neu zu rendern). Die API-Daten werden als JSON im HTML eingebettetder Component hat sofort echte Daten ohne Client-Fetch.
43
43
 
44
44
  ```bash
45
45
  # SSR-HTML holen (z.B. serverseitig per fetch, file_get_contents, ESI)
@@ -47,6 +47,8 @@ curl "https://scs-api.vercel.app/api/header/render?lang=de&large-animation"
47
47
  curl "https://scs-api.vercel.app/api/footer/render?lang=de"
48
48
  ```
49
49
 
50
+ Siehe Framework-spezifische Beispiele: [React / Next.js](#react--nextjs-app-router), [Vue / Nuxt](#vue--nuxt-3), [PHP / Vanilla](#php--vanilla-html)
51
+
50
52
  ## API
51
53
 
52
54
  ### `<startnext-header>`
@@ -56,8 +58,8 @@ curl "https://scs-api.vercel.app/api/footer/render?lang=de"
56
58
  | Attribute | Type | Default | Description |
57
59
  |---|---|---|---|
58
60
  | `lang` | `string` | `'de'` | Sprache (`'de'`, `'en'`) |
59
- | `light` | `boolean` | `false` | Heller Header (weiße Schrift/Icons über dunklem Hero, wechselt automatisch zu dunkel beim Scrollen). Standard ohne Attribut: dunkler Header |
60
- | `large-animation` | `boolean` | `false` | Große Lottie-Logo-Animation mit Claim und horizontaler Navigation |
61
+ | `light` | `boolean` | `false` | Heller Header (weisse Schrift/Icons ueber dunklem Hero, wechselt automatisch zu dunkel beim Scrollen). Standard ohne Attribut: dunkler Header |
62
+ | `large-animation` | `boolean` | `false` | Grosse Lottie-Logo-Animation mit Claim und horizontaler Navigation |
61
63
  | `authenticated` | `boolean` | `false` | Zeigt User-Avatar statt "Anmelden" Button |
62
64
  | `user-name` | `string` | - | Name des eingeloggten Users |
63
65
  | `user-avatar` | `string` | - | Avatar-URL des Users |
@@ -68,34 +70,34 @@ curl "https://scs-api.vercel.app/api/footer/render?lang=de"
68
70
  | `show-back-link` | `boolean` | `false` | Zeigt einen "Zurueck zu Startnext"-Link (Text kommt von der API) |
69
71
  | `back-url` | `string` | - | Ueberschreibt die Back-Link-URL |
70
72
  | `back-label` | `string` | - | Ueberschreibt den Back-Link-Text |
71
- | `api-url` | `string` | - | API-Endpoint-URL fuer Live-Daten aus Notion |
73
+ | `api-url` | `string` | - | API-Endpoint-URL fuer Live-Daten aus Notion (nicht noetig bei SSR) |
72
74
 
73
75
  **Events:**
74
76
 
75
77
  | Event | Detail | Description |
76
78
  |---|---|---|
77
- | `burger-menu-toggle` | `{ open: boolean }` | Burger-Menü geöffnet/geschlossen |
78
- | `user-menu-toggle` | `{ open: boolean }` | User-Menü geöffnet/geschlossen |
79
+ | `burger-menu-toggle` | `{ open: boolean }` | Burger-Menue geoeffnet/geschlossen |
80
+ | `user-menu-toggle` | `{ open: boolean }` | User-Menue geoeffnet/geschlossen |
79
81
  | `navigation-click` | `{ item: NavigationItem }` | Navigation-Link geklickt |
80
82
  | `cta-click` | `{ url: string }` | "Projekt starten" Button geklickt |
81
83
  | `logout` | `{}` | Abmelden geklickt |
82
84
  | `language-change` | `{ language: string }` | Sprache gewechselt |
83
- | `scroll-state-change` | `{ scrolled: boolean, slideUp: boolean }` | Scroll-Zustand geändert |
85
+ | `scroll-state-change` | `{ scrolled: boolean, slideUp: boolean }` | Scroll-Zustand geaendert |
84
86
  | `color-mode-change` | `{ mode: 'light' \| 'dark' }` | Farbmodus gewechselt (per Toggle-Button) |
85
87
 
86
88
  **Usage Examples:**
87
89
 
88
90
  ```html
89
- <!-- Default: dunkler Header (dunkle Schrift für helle Seiten) -->
91
+ <!-- Default: dunkler Header (dunkle Schrift fuer helle Seiten) -->
90
92
  <startnext-header></startnext-header>
91
93
 
92
- <!-- Heller Header (weiße Schrift über dunklem Hero) -->
94
+ <!-- Heller Header (weisse Schrift ueber dunklem Hero) -->
93
95
  <startnext-header light></startnext-header>
94
96
 
95
- <!-- Heller Header + große Animation (z.B. Startseite) -->
97
+ <!-- Heller Header + grosse Animation (z.B. Startseite) -->
96
98
  <startnext-header light large-animation></startnext-header>
97
99
 
98
- <!-- Heller Header + groß + eingeloggt -->
100
+ <!-- Heller Header + gross + eingeloggt -->
99
101
  <startnext-header
100
102
  light
101
103
  large-animation
@@ -156,7 +158,7 @@ header.addEventListener('color-mode-change', (e) => {
156
158
  | Attribute | Type | Default | Description |
157
159
  |---|---|---|---|
158
160
  | `lang` | `string` | `'de'` | Sprache (`'de'`, `'en'`) |
159
- | `api-url` | `string` | - | API-Endpoint-URL fuer Live-Daten |
161
+ | `api-url` | `string` | - | API-Endpoint-URL fuer Live-Daten (nicht noetig bei SSR) |
160
162
  | `lang-sync` | `boolean` | `true` | Synchronisiert Sprache automatisch wenn der Header ein `language-change` Event emittiert. Mit `lang-sync="false"` deaktivieren |
161
163
 
162
164
  **Events:**
@@ -184,7 +186,7 @@ startnext-footer {
184
186
 
185
187
  ## Fonts
186
188
 
187
- Standardmäßig nutzen die Components System-Fonts als Fallback. Für eigene Schriftarten `@font-face` im eigenen CSS deklarieren und `--font-family` setzen:
189
+ Standardmaessig nutzen die Components System-Fonts als Fallback. Fuer eigene Schriftarten `@font-face` im eigenen CSS deklarieren und `--font-family` setzen:
188
190
 
189
191
  ```css
190
192
  @font-face {
@@ -202,68 +204,74 @@ startnext-header, startnext-footer {
202
204
 
203
205
  ## Server-Side Rendering (SSR)
204
206
 
205
- Das Paket exportiert einen server-sicheren Subpath `@startnext/chrome/render` mit reinen String-Funktionen (keine Browser-Abhaengigkeiten). Damit kann der API-Server fertig gerendertes HTML per Declarative Shadow DOM erzeugen.
206
-
207
- ### Render Entry Point
207
+ Die API liefert fertig gerendertes HTML mit **Declarative Shadow DOM** (`<template shadowrootmode="open">`) und eingebetteten **API-Daten als JSON** (`<script data-ssr-props>`). Der Browser zeigt den Content sofort an ohne JavaScript. Die Web Component JS hydriert anschliessend (Events anbinden, kein Re-Render noetig).
208
208
 
209
- ```javascript
210
- import {
211
- renderHeader, renderBurgerItems, renderUserItems,
212
- renderFooter,
213
- headerCSS, footerCSS,
214
- renderHeaderCrawlerNav, renderFooterCrawlerNav,
215
- toHeaderRenderData, toFooterRenderData,
216
- getUiString, getIcon,
217
- } from '@startnext/chrome/render';
218
- ```
209
+ ### Wie es funktioniert
219
210
 
220
- ### Exports
221
-
222
- | Export | Beschreibung |
223
- |--------|-------------|
224
- | `renderHeader(props)` | Header Shadow DOM HTML |
225
- | `renderBurgerItems(data, expandedSections)` | Burger-Menue Items HTML |
226
- | `renderUserItems(data, expandedSections)` | User-Menue Items HTML |
227
- | `renderFooter(data)` | Footer Shadow DOM HTML |
228
- | `headerCSS`, `footerCSS`, `resetCSS` | CSS als Strings |
229
- | `getIcon(name, size?)` | SVG-Icon als String |
230
- | `getUiString(key, lang)` | UI-String (ARIA Labels etc.) |
231
- | `toHeaderRenderData(apiData)` | API-Daten → HeaderData (fuegt stub `theme` hinzu) |
232
- | `toFooterRenderData(apiData)` | API-Daten → FooterData (fuegt stub `theme` hinzu) |
233
- | `renderHeaderCrawlerNav(data)` | Crawler-`<nav>` fuer Header (Light DOM, SEO) |
234
- | `renderFooterCrawlerNav(data)` | Crawler-`<nav>` fuer Footer (Light DOM, SEO) |
235
-
236
- ### Hydration
237
-
238
- Wenn der Server HTML mit Declarative Shadow DOM (`<template shadowrootmode="open">`) liefert, erkennt die Web Component den bestehenden `shadowRoot` und **hydriert** statt neu zu rendern:
239
-
240
- 1. Browser parst DSD → `shadowRoot` existiert vor dem Custom-Element-Constructor
241
- 2. `BaseComponent` erkennt bestehenden `shadowRoot`, setzt `isHydrating = true`
242
- 3. `render()` ueberspringt `innerHTML`, ruft nur `attachEvents()` auf
243
- 4. `connectedCallback()` erkennt bestehenden `[data-crawler-nav]`, ueberspringt `renderLightDomNav()`
244
- 5. `loadApiData()` holt frische Daten → loest danach ein vollstaendiges Re-Render aus
211
+ 1. Server fetcht SSR-HTML von `/api/header/render` bzw. `/api/footer/render`
212
+ 2. HTML wird direkt in die Seite eingebunden
213
+ 3. Browser parst DSD → Shadow DOM existiert sofort, Content ist sichtbar
214
+ 4. Web Component JS laedt → erkennt bestehenden `shadowRoot`, setzt `isHydrating = true`
215
+ 5. `render()` ueberspringt `innerHTML`, ruft nur `attachEvents()` auf
216
+ 6. `parseSsrProps()` liest eingebettetes JSON → populiert API-Cache mit echten Daten
217
+ 7. Attribut-Aenderungen (z.B. `color-mode`, `large-animation`) funktionieren sofort — kein Client-Fetch noetig
245
218
 
246
219
  ```html
247
- <!-- Server liefert fertiges HTML -->
220
+ <!-- Server liefert fertiges HTML (vereinfacht) -->
248
221
  <startnext-header lang="de" light large-animation>
249
222
  <template shadowrootmode="open">
250
223
  <style>/* headerCSS */</style>
251
224
  <div><!-- Shadow DOM HTML --></div>
252
225
  </template>
253
- <nav data-crawler-nav aria-hidden="true" style="display:none">
254
- <!-- SEO-Links -->
255
- </nav>
226
+ <script type="application/json" data-ssr-props>{"mainNavigation":[...],...}</script>
256
227
  </startnext-header>
257
228
 
258
229
  <!-- JS hydriert automatisch -->
259
230
  <script type="module" src="@startnext/chrome"></script>
260
231
  ```
261
232
 
262
- ### SSR mit React / Next.js (App Router)
233
+ ### Render-Endpoints (Query-Parameter)
234
+
235
+ **Header:** `GET /api/header/render`
236
+
237
+ | Parameter | Typ | Beschreibung |
238
+ |-----------|-----|-------------|
239
+ | `lang` | `de` \| `en` | Sprache |
240
+ | `light` | flag | Heller Header |
241
+ | `large-animation` | flag | Grosse Logo-Animation |
242
+ | `authenticated` | flag | Eingeloggt |
243
+ | `user-name` | string | User-Name |
244
+ | `user-avatar` | string | Avatar-URL |
245
+ | `hide-color-mode` | flag | Color-Mode Toggle verstecken |
246
+ | `hide-lang` | flag | Language-Switcher verstecken |
247
+ | `hide-login` | flag | Login-Button verstecken |
248
+ | `show-back-link` | flag | Back-Link anzeigen |
249
+ | `back-url` | string | Back-Link URL |
250
+ | `back-label` | string | Back-Link Text |
251
+
252
+ **Footer:** `GET /api/footer/render`
253
+
254
+ | Parameter | Typ | Beschreibung |
255
+ |-----------|-----|-------------|
256
+ | `lang` | `de` \| `en` | Sprache |
257
+
258
+ Beispiel: `GET /api/header/render?lang=de&large-animation&hide-login&show-back-link`
259
+
260
+ ### `api-url` bei SSR nicht noetig
261
+
262
+ Bei SSR werden die API-Daten als JSON im HTML eingebettet (`<script data-ssr-props>`). Der Component liest das beim Hydrating und hat sofort echte Daten im Cache. `api-url` ist daher **nicht noetig** — der Component rendert und reagiert auf Attribut-Aenderungen ohne zusaetzlichen Fetch.
263
+
264
+ `api-url` ist nur fuer **Client-Side-Only** Rendering relevant, wenn kein SSR-HTML verfuegbar ist.
265
+
266
+ ---
267
+
268
+ ## Framework-Integration (SSR)
263
269
 
264
- React's `dangerouslySetInnerHTML` nutzt intern `innerHTML`, das Declarative Shadow DOM **nicht** parsed. Daher muss nach der Hydration `setHTMLUnsafe()` als Fallback aufgerufen werden.
270
+ ### React / Next.js (App Router)
265
271
 
266
- **1. Layout (Server Component) SSR HTML von der API fetchen:**
272
+ React's `dangerouslySetInnerHTML` nutzt `innerHTML`, das DSD nicht parsed. Nach der Hydration wird `setHTMLUnsafe()` als Fallback aufgerufen.
273
+
274
+ **Layout (Server Component):**
267
275
 
268
276
  ```tsx
269
277
  // app/[locale]/layout.tsx
@@ -298,7 +306,7 @@ export default async function Layout({ children, params }) {
298
306
  }
299
307
  ```
300
308
 
301
- **2. Client Component — SSR HTML rendern + DSD Fallback + Hydration:**
309
+ **Header (Client Component):**
302
310
 
303
311
  ```tsx
304
312
  // components/ChromeHeader.tsx
@@ -312,50 +320,205 @@ export function ChromeHeader({ ssrHtml }: { ssrHtml?: string }) {
312
320
  useEffect(() => {
313
321
  const container = containerRef.current;
314
322
  if (container && ssrHtml) {
315
- // Falls DSD nicht geparsed wurde (innerHTML parsed kein DSD),
316
- // setHTMLUnsafe() als Fallback nutzen
317
323
  const header = container.querySelector("startnext-header");
318
324
  if (!header?.shadowRoot && "setHTMLUnsafe" in Element.prototype) {
319
325
  container.setHTMLUnsafe(ssrHtml);
320
326
  }
321
327
  }
322
- import("@startnext/chrome"); // Web Component JS laden → Hydration
328
+ import("@startnext/chrome");
323
329
  setMounted(true);
324
330
  }, [ssrHtml]);
325
331
 
332
+ // Attribut-Sync nach Mount (z.B. color-mode, large-animation)
333
+ useEffect(() => {
334
+ const el = containerRef.current?.querySelector("startnext-header");
335
+ if (!el || !mounted) return;
336
+ // Beispiel: el.setAttribute("color-mode", resolvedTheme);
337
+ }, [mounted]);
338
+
326
339
  if (ssrHtml) {
327
340
  return (
328
- <div
329
- ref={containerRef}
330
- style={{ display: "contents" }}
341
+ <div ref={containerRef} style={{ display: "contents" }}
331
342
  suppressHydrationWarning
332
343
  dangerouslySetInnerHTML={{ __html: ssrHtml }}
333
344
  />
334
345
  );
335
346
  }
336
347
 
337
- // Fallback: Client-Side Rendering (wenn SSR HTML nicht verfuegbar)
348
+ // Fallback ohne SSR
338
349
  return <startnext-header api-url={process.env.NEXT_PUBLIC_SCS_API_URL} />;
339
350
  }
340
351
  ```
341
352
 
342
- **Wichtig:** `dangerouslySetInnerHTML` sorgt dafuer, dass das SSR-HTML im initialen HTML-Stream enthalten ist (fuer Crawler und First Paint). `setHTMLUnsafe()` stellt sicher, dass DSD auch nach React-Hydration oder Client-Side-Navigation korrekt geparsed wird.
353
+ **Footer (Client Component):**
343
354
 
344
- **Render-Endpoints (Query-Parameter):**
355
+ ```tsx
356
+ // components/ChromeFooter.tsx
357
+ "use client";
358
+ import { useEffect, useRef } from "react";
345
359
 
346
- | Parameter | Typ | Beschreibung |
347
- |-----------|-----|-------------|
348
- | `lang` | `de` \| `en` | Sprache |
349
- | `light` | flag | Heller Header |
350
- | `large-animation` | flag | Grosse Logo-Animation |
351
- | `hide-color-mode` | flag | Color-Mode Toggle verstecken |
352
- | `hide-lang` | flag | Language-Switcher verstecken |
353
- | `hide-login` | flag | Login-Button verstecken |
354
- | `show-back-link` | flag | Back-Link anzeigen |
355
- | `back-url` | string | Back-Link URL |
356
- | `back-label` | string | Back-Link Text |
360
+ export function ChromeFooter({ ssrHtml }: { ssrHtml?: string }) {
361
+ const containerRef = useRef<HTMLDivElement>(null);
357
362
 
358
- Beispiel: `GET /api/header/render?lang=de&large-animation&hide-login&show-back-link`
363
+ useEffect(() => {
364
+ const container = containerRef.current;
365
+ if (container && ssrHtml) {
366
+ const footer = container.querySelector("startnext-footer");
367
+ if (!footer?.shadowRoot && "setHTMLUnsafe" in Element.prototype) {
368
+ container.setHTMLUnsafe(ssrHtml);
369
+ }
370
+ }
371
+ import("@startnext/chrome");
372
+ }, [ssrHtml]);
373
+
374
+ if (ssrHtml) {
375
+ return (
376
+ <div ref={containerRef} style={{ display: "contents" }}
377
+ suppressHydrationWarning
378
+ dangerouslySetInnerHTML={{ __html: ssrHtml }}
379
+ />
380
+ );
381
+ }
382
+
383
+ return <startnext-footer api-url={process.env.NEXT_PUBLIC_SCS_API_URL} />;
384
+ }
385
+ ```
386
+
387
+ ### Vue / Nuxt 3
388
+
389
+ Vue parsed DSD korrekt via `v-html` — kein `setHTMLUnsafe()` Workaround noetig.
390
+
391
+ **Server Plugin (SSR-HTML fetchen):**
392
+
393
+ ```ts
394
+ // server/plugins/chrome.ts
395
+ export default defineEventHandler(async (event) => {
396
+ // Wird automatisch von useAsyncData / useFetch aufgerufen
397
+ });
398
+ ```
399
+
400
+ **Layout:**
401
+
402
+ ```vue
403
+ <!-- layouts/default.vue -->
404
+ <script setup lang="ts">
405
+ const { locale } = useI18n();
406
+ const config = useRuntimeConfig();
407
+
408
+ const { data: headerHtml } = await useFetch(
409
+ () => `${config.public.scsApiUrl}/api/header/render?lang=${locale.value}&large-animation`,
410
+ { key: `chrome-header-${locale.value}` }
411
+ );
412
+
413
+ const { data: footerHtml } = await useFetch(
414
+ () => `${config.public.scsApiUrl}/api/footer/render?lang=${locale.value}`,
415
+ { key: `chrome-footer-${locale.value}` }
416
+ );
417
+
418
+ onMounted(() => {
419
+ import('@startnext/chrome');
420
+ });
421
+ </script>
422
+
423
+ <template>
424
+ <div v-if="headerHtml" v-html="headerHtml" style="display: contents" />
425
+ <startnext-header v-else :lang="locale" :api-url="config.public.scsApiUrl" />
426
+
427
+ <main>
428
+ <slot />
429
+ </main>
430
+
431
+ <div v-if="footerHtml" v-html="footerHtml" style="display: contents" />
432
+ <startnext-footer v-else :lang="locale" :api-url="config.public.scsApiUrl" />
433
+ </template>
434
+ ```
435
+
436
+ **Nuxt Config (Custom Elements registrieren):**
437
+
438
+ ```ts
439
+ // nuxt.config.ts
440
+ export default defineNuxtConfig({
441
+ vue: {
442
+ compilerOptions: {
443
+ isCustomElement: (tag) => tag.startsWith('startnext-'),
444
+ },
445
+ },
446
+ runtimeConfig: {
447
+ public: {
448
+ scsApiUrl: process.env.SCS_API_URL || 'https://scs-api.vercel.app',
449
+ },
450
+ },
451
+ });
452
+ ```
453
+
454
+ ### PHP / Vanilla HTML
455
+
456
+ ```php
457
+ <?php
458
+ $apiUrl = 'https://scs-api.vercel.app';
459
+ $lang = 'de';
460
+
461
+ $headerHtml = @file_get_contents("$apiUrl/api/header/render?lang=$lang&large-animation");
462
+ $footerHtml = @file_get_contents("$apiUrl/api/footer/render?lang=$lang");
463
+ ?>
464
+ <!DOCTYPE html>
465
+ <html lang="<?= $lang ?>">
466
+ <head>
467
+ <script type="module" src="https://unpkg.com/@startnext/chrome@latest/dist/index.js"></script>
468
+ </head>
469
+ <body>
470
+ <?= $headerHtml ?: '<startnext-header api-url="' . $apiUrl . '"></startnext-header>' ?>
471
+
472
+ <main><!-- Content --></main>
473
+
474
+ <?= $footerHtml ?: '<startnext-footer api-url="' . $apiUrl . '"></startnext-footer>' ?>
475
+ </body>
476
+ </html>
477
+ ```
478
+
479
+ ### Edge Side Includes (ESI)
480
+
481
+ ```html
482
+ <!-- Varnish / CDN ESI -->
483
+ <esi:try>
484
+ <esi:attempt>
485
+ <esi:include src="/api/header/render?lang=de&large-animation" />
486
+ </esi:attempt>
487
+ <esi:except>
488
+ <startnext-header api-url="https://scs-api.vercel.app"></startnext-header>
489
+ </esi:except>
490
+ </esi:try>
491
+ ```
492
+
493
+ ---
494
+
495
+ ## Render Entry Point (Server-seitig)
496
+
497
+ Das Paket exportiert einen server-sicheren Subpath `@startnext/chrome/render` mit reinen String-Funktionen (keine Browser-Abhaengigkeiten):
498
+
499
+ ```javascript
500
+ import {
501
+ renderHeader, renderBurgerItems, renderUserItems,
502
+ renderFooter,
503
+ headerCSS, footerCSS,
504
+ toHeaderRenderData, toFooterRenderData,
505
+ getUiString, getIcon,
506
+ } from '@startnext/chrome/render';
507
+ ```
508
+
509
+ | Export | Beschreibung |
510
+ |--------|-------------|
511
+ | `renderHeader(props)` | Header Shadow DOM HTML |
512
+ | `renderBurgerItems(data, expandedSections)` | Burger-Menue Items HTML |
513
+ | `renderUserItems(data, expandedSections)` | User-Menue Items HTML |
514
+ | `renderFooter(data)` | Footer Shadow DOM HTML |
515
+ | `headerCSS`, `footerCSS`, `resetCSS` | CSS als Strings |
516
+ | `getIcon(name, size?)` | SVG-Icon als String |
517
+ | `getUiString(key, lang)` | UI-String (ARIA Labels etc.) |
518
+ | `toHeaderRenderData(apiData)` | API-Daten -> HeaderData (fuegt stub `theme` hinzu) |
519
+ | `toFooterRenderData(apiData)` | API-Daten -> FooterData (fuegt stub `theme` hinzu) |
520
+ | `renderHeaderCrawlerNav(data)` | Crawler-`<nav>` fuer Header (optional, fuer Client-Side Rendering) |
521
+ | `renderFooterCrawlerNav(data)` | Crawler-`<nav>` fuer Footer (optional, fuer Client-Side Rendering) |
359
522
 
360
523
  ## Browser Support
361
524
 
@@ -417,14 +580,13 @@ src/
417
580
  - Shadow DOM (open mode), kein Virtual DOM, Declarative Shadow DOM fuer SSR
418
581
 
419
582
  **Zwei Entry Points:**
420
- - `src/index.ts` `dist/index.js` (ESM) + `dist/index.umd.js` (UMD) — Browser, registriert Custom Elements
421
- - `src/render.ts` `dist/render.js` (ESM) — Server, reine String-Funktionen ohne Browser-Abhaengigkeiten
583
+ - `src/index.ts` -> `dist/index.js` (ESM) + `dist/index.umd.js` (UMD) — Browser, registriert Custom Elements
584
+ - `src/render.ts` -> `dist/render.js` (ESM) — Server, reine String-Funktionen ohne Browser-Abhaengigkeiten
422
585
 
423
586
  ## Monorepo Structure
424
587
 
425
588
  ```
426
589
  packages/
427
- scs-web-component/ dieses Paket
428
- scs-api/ Express API Service (nutzt @startnext/chrome/render fuer SSR)
590
+ scs-web-component/ <- dieses Paket
591
+ scs-api/ <- Express API Service (nutzt @startnext/chrome/render fuer SSR)
429
592
  ```
430
-
@@ -4,8 +4,6 @@ export declare class StartnextFooter extends BaseComponent {
4
4
  private data;
5
5
  /** Cached API data per language. */
6
6
  private apiCache;
7
- /** True while SSR content should be preserved (between hydration and first API data load). */
8
- private _preserveSsrContent;
9
7
  /** Bound handler for document-level language-change events. */
10
8
  private _langSyncHandler;
11
9
  private get currentLang();
@@ -17,6 +15,9 @@ export declare class StartnextFooter extends BaseComponent {
17
15
  private teardownLangSync;
18
16
  /** Return cached API data if available, otherwise defaults (theme only). */
19
17
  private getFooterData;
18
+ /** Parse SSR-embedded data to populate the API cache during hydration.
19
+ * Eliminates the need for a client-side fetch when SSR HTML is used. */
20
+ private parseSsrProps;
20
21
  /** If api-url is set, fetch footer data from API and re-render. */
21
22
  private loadApiData;
22
23
  private renderLightDomNav;
@@ -1 +1 @@
1
- {"version":3,"file":"StartnextFooter.d.ts","sourceRoot":"","sources":["../../../src/components/footer/StartnextFooter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAMzD,qBAAa,eAAgB,SAAQ,aAAa;IAChD,MAAM,CAAC,kBAAkB,WAAoC;IAE7D,OAAO,CAAC,IAAI,CAAc;IAC1B,oCAAoC;IACpC,OAAO,CAAC,QAAQ,CAAiC;IACjD,8FAA8F;IAC9F,OAAO,CAAC,mBAAmB,CAAS;IACpC,+DAA+D;IAC/D,OAAO,CAAC,gBAAgB,CAAqC;IAE7D,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,eAAe,GAE1B;IAED,iBAAiB,IAAI,IAAI;IAmBzB,oBAAoB,IAAI,IAAI;IAK5B,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAqB5C,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,gBAAgB;IAOxB,4EAA4E;IAC5E,OAAO,CAAC,aAAa;IAMrB,mEAAmE;YACrD,WAAW;IAwBzB,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,YAAY;CAUrB"}
1
+ {"version":3,"file":"StartnextFooter.d.ts","sourceRoot":"","sources":["../../../src/components/footer/StartnextFooter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAMzD,qBAAa,eAAgB,SAAQ,aAAa;IAChD,MAAM,CAAC,kBAAkB,WAAoC;IAE7D,OAAO,CAAC,IAAI,CAAc;IAC1B,oCAAoC;IACpC,OAAO,CAAC,QAAQ,CAAiC;IACjD,+DAA+D;IAC/D,OAAO,CAAC,gBAAgB,CAAqC;IAE7D,OAAO,KAAK,WAAW,GAEtB;IAED,OAAO,KAAK,eAAe,GAE1B;IAED,iBAAiB,IAAI,IAAI;IAiBzB,oBAAoB,IAAI,IAAI;IAK5B,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAe5C,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,gBAAgB;IAOxB,4EAA4E;IAC5E,OAAO,CAAC,aAAa;IAMrB;6EACyE;IACzE,OAAO,CAAC,aAAa;IAWrB,mEAAmE;YACrD,WAAW;IAuBzB,OAAO,CAAC,iBAAiB;IAiCzB,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,YAAY;CAUrB"}
@@ -12,9 +12,6 @@ export declare class StartnextHeader extends BaseComponent {
12
12
  private pendingRaf;
13
13
  /** Cached API data per language, so we don't re-fetch on every re-render. */
14
14
  private apiCache;
15
- /** True while SSR content should be preserved (between hydration and first API data load).
16
- * Prevents attribute changes from triggering a full re-render with empty default data. */
17
- private _preserveSsrContent;
18
15
  private handleScrollBound;
19
16
  private handleKeydownBound;
20
17
  private handleResizeBound;
@@ -28,6 +25,9 @@ export declare class StartnextHeader extends BaseComponent {
28
25
  connectedCallback(): void;
29
26
  /** Return cached API data if available, otherwise defaults (theme only). */
30
27
  private getHeaderData;
28
+ /** Parse SSR-embedded data to populate the API cache during hydration.
29
+ * Eliminates the need for a client-side fetch when SSR HTML is used. */
30
+ private parseSsrProps;
31
31
  /** If api-url is set, fetch header data from API and re-render. */
32
32
  private loadApiData;
33
33
  disconnectedCallback(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"StartnextHeader.d.ts","sourceRoot":"","sources":["../../../src/components/header/StartnextHeader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAWzD,qBAAa,eAAgB,SAAQ,aAAa;IAChD,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,UAAU,CAAuB;IACzC,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAiC;IACjD;+FAC2F;IAC3F,OAAO,CAAC,mBAAmB,CAAS;IAEpC,OAAO,CAAC,iBAAiB,CAAgC;IACzD,OAAO,CAAC,kBAAkB,CAAiC;IAC3D,OAAO,CAAC,iBAAiB,CAAgC;IACzD,OAAO,CAAC,mBAAmB,CAAkC;IAE7D,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,KAAK,WAAW,GAEtB;IAED,6EAA6E;IAC7E,OAAO,KAAK,aAAa,GAGxB;IAED,OAAO,CAAC,EAAE;IAIV,iBAAiB,IAAI,IAAI;IA4BzB,4EAA4E;IAC5E,OAAO,CAAC,aAAa;IAMrB,mEAAmE;YACrD,WAAW;IA8BzB,oBAAoB,IAAI,IAAI;IAU5B,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAqB5C,gEAAgE;IAChE,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,UAAU;IA0BlB,OAAO,CAAC,YAAY;IAgDpB,OAAO,CAAC,mBAAmB;IAoC3B,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,iBAAiB;IAsDzB,OAAO,CAAC,MAAM;IAuCd,OAAO,CAAC,YAAY;CAkErB"}
1
+ {"version":3,"file":"StartnextHeader.d.ts","sourceRoot":"","sources":["../../../src/components/header/StartnextHeader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAWzD,qBAAa,eAAgB,SAAQ,aAAa;IAChD,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,UAAU,CAAuB;IACzC,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAiC;IAEjD,OAAO,CAAC,iBAAiB,CAAgC;IACzD,OAAO,CAAC,kBAAkB,CAAiC;IAC3D,OAAO,CAAC,iBAAiB,CAAgC;IACzD,OAAO,CAAC,mBAAmB,CAAkC;IAE7D,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,OAAO,CAAC,aAAa,CAAS;IAE9B,OAAO,KAAK,WAAW,GAEtB;IAED,6EAA6E;IAC7E,OAAO,KAAK,aAAa,GAGxB;IAED,OAAO,CAAC,EAAE;IAIV,iBAAiB,IAAI,IAAI;IAuBzB,4EAA4E;IAC5E,OAAO,CAAC,aAAa;IAMrB;6EACyE;IACzE,OAAO,CAAC,aAAa;IAWrB,mEAAmE;YACrD,WAAW;IA6BzB,oBAAoB,IAAI,IAAI;IAU5B,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAa5C,gEAAgE;IAChE,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,UAAU;IA0BlB,OAAO,CAAC,YAAY;IAgDpB,OAAO,CAAC,mBAAmB;IAoC3B,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,iBAAiB;IAsDzB,OAAO,CAAC,MAAM;IAuCd,OAAO,CAAC,YAAY;CAkErB"}