Alles unten lebt unter `duxt` in deiner `app/app.config.ts`. Wie die
Zusammenführung funktioniert, und warum Arrays ersetzen statt anzuhängen, steht
in [Konfiguration](/getting-started/configuration).

## Seite

| Schlüssel          | Standard             | Was er steuert                                          |
| :----------------- | :------------------- | :------------------------------------------------------- |
| `title`            | **keiner**           | Der Name in der Navigationsleiste und im SEO-Titel        |
| `logo`              | **keiner**           | Eine Wortmarke statt des Paars aus Icon und Name — siehe unten |
| `version`          | **keine**            | Das Abzeichen daneben                                     |
| `organization`     | **keine**            | Wer die Seite herausgibt, für schema.org — siehe unten    |
| `locales`          | alle sieben          | Welche Sprachen der Ebene diese Seite bedient             |
| `breadcrumb`       | `true`               | `false` entfernt die Spur über dem Seitentitel            |
| `pageIcon`          | **keins**            | Icon für Seiten ohne eigenes im Frontmatter — das der Sektion gewinnt |
| `packageManagers`  | pnpm, npm, yarn, bun | Welche Manager ein Befehlsblock anbietet, in dieser Reihenfolge |
| `requestSamples`   | sieben von zwölf   | Welche Code-Samples der Try-it-Client anbietet — siehe [Request-Samples](/reference/openapi/request-samples) |
| `sampleLanguages`  | **keine**          | Zusätzliche Shiki-Grammatiken, für `x-codeSamples` in einer Sprache, die kein Sample nennt |
| `poweredBy`        | an                   | `false` entfernt die Zeile „Powered by duxt“ aus dem Fußbereich |

## Verhalten

| Schlüssel | Standard | Wirkung |
| :-- | :-- | :-- |
| `copy.models` | ChatGPT, Claude | Zielmodelle in Menüreihenfolge; die Liste ersetzt den Standard |
| `contributors.avatarUrl` | GitHub-Template | `{username}` wird je Contributor ersetzt |
| `toc.depth` | `3` | Tiefste Überschrift im Inhaltsverzeichnis |
| `toc.scrollOffset` | `96` | Aktivierungslinie unter dem Viewport-Rand |
| `search.fuzzy` | `0.35`, `3`, `20` | Fuse-Schwelle, Mindestlänge und Ergebnislimit |
| `search.recentPages` | `5` | Letzte Seiten; `0` blendet die Liste aus |
| `openapi.exampleDepth` | `6` | Maximale Tiefe abgeleiteter Beispiele |
| `openapi.schemaDepth` | `8` | Maximale Tiefe des Schema-Baums |
| `shortcuts.singleCharacter` | `true` | Globale `?`, `[` und `]`; `⌘/Strg+K` bleibt |
| `analytics.track` | **keine** | Provider-neutrales Callback je Leserinteraktion |

```ts [app/app.config.ts]
export default defineAppConfig({
  duxt: {
    copy: {
      models: [
        {
          label: 'Mein Assistent',
          icon: 'simple-icons:openai',
          url: 'https://example.com/new?prompt='
        }
      ]
    },
    contributors: {
      avatarUrl: 'https://github.com/{username}.png?size=96'
    },
    toc: { depth: 4, scrollOffset: 112 },
    search: {
      fuzzy: { threshold: 0.3, minMatchCharLength: 3, limit: 15 },
      recentPages: 8
    },
    openapi: { exampleDepth: 6, schemaDepth: 8 },
    shortcuts: { singleCharacter: false }
  }
})
```

Listen ersetzen ihre Defaults. Eine leere `copy.models`-Liste entfernt externe
Modellziele, aber nicht Kopieren und Quellansicht. `search.recentPages: 0`
speichert keine neuen Einträge. Fuzzy-Optionen gelten nur für Beinahetreffer,
nachdem die exakte Content-Suche leer blieb. Analytics sendet ohne Callback
nichts; Einwilligung, Provider und Speicherung gehören der konsumierenden Site.

### `logo`

| Feld      | Typ      | Hinweise                                                     |
| :-------- | :------- | :------------------------------------------------------------ |
| `src`     | `string` | Die Marke. Ohne Wert bleibt das generische Icon neben `title` |
| `srcDark` | `string` | Wird per CSS unter der `dark`-Klasse getauscht, nicht per Skript |
| `alt`     | Text     | Fällt auf `title` zurück                                     |

Wie du eine setzt, steht in [Deiner Website eine Marke geben](/guides/brand-your-site).

### `organization`

Der `Organization`-Knoten, den Suchmaschinen und KI-Zusammenfassungen lesen, um
eine Quelle zu benennen. Abwesend, bis du ihn setzt: duxt rendert die
Dokumentation anderer und rät nicht, wessen.

| Feld   | Typ      | Hinweise                                                        |
| :----- | :------- | :---------------------------------------------------------------- |
| `name` | Text     | Der Name der Herausgeberin. Ohne ihn wird nichts veröffentlicht  |
| `url`  | `string` | Deren Seite. Fällt auf `site.url` zurück                         |
| `logo` | `string` | Absolut oder ein Pfad ab der Wurzel; quadratisch und ab 112px    |


## Ankündigungen

Ein seitenweiter Hinweis — etwa für ein Release, eine Migration oder ein
Wartungsfenster. Solange du keinen Eintrag schreibst, wird nichts gerendert und
kein Platz dafür freigehalten.

```ts [app/app.config.ts]
announcements: [
  {
    text: { 'en-GB': 'Version 2 is out.', 'de-DE': 'Version 2 ist da.' },
    link: { label: 'Release Notes lesen', to: '/releases/v2' }
  },
  {
    id: 'march-maintenance',
    text: 'Geplante Wartung am Sonntag ab 09:00 UTC.',
    placement: 'below-header',
    startsAt: '2026-03-01T09:00:00Z',
    endsAt: '2026-03-08T09:00:00Z'
  }
],
announcementOptions: { maxVisible: 1 }
```

Die Liste wird in ihrer Reihenfolge gezeichnet: Release und Störung sind zwei
Ankündigungen, und das Ende der einen darf die andere nicht mit entfernen.

| Feld        | Typ                                               | Hinweise                                                         |
| :---------- | :------------------------------------------------ | :--------------------------------------------------------------- |
| `text`      | Text                                              | Satz als Literal, i18n-Key oder Locale-Record                   |
| `link`      | Link                                              | Eine Aktion; weitere gehören auf die verlinkte Seite            |
| `placement` | `above-header`, `below-header` oder `above-content` | Position; Standard ist `above-header`                           |
| `startsAt`  | `string`                                          | Beginn; alles, was `Date` parsen kann                            |
| `endsAt`    | `string`                                          | Ende                                                             |
| `id`        | `string`                                          | Identität fürs Schließen; sonst aus Inhalt und Link abgeleitet |

### Wo sie erscheinen

| Platzierung      | Ort                                                               |
| :--------------- | :---------------------------------------------------------------- |
| `above-header`   | Volle Breite am Browserrand über der Navigation — der Standard |
| `below-header`   | Volle Breite unter der Navigation, über Sidebars und Seite     |
| `above-content`  | In der Dokumentspalte bei Versions- und Übersetzungshinweisen   |

`above-content` gehört zur Dokumentspalte und erscheint deshalb auf
Dokumentationsseiten, nicht auf einer Landingpage ohne diese Spalte.

`announcementOptions.maxVisible` begrenzt, wie viele Hinweise eine Platzierung
gleichzeitig zeigt. Eine Zahl gilt für **jede** Platzierung, ein Objekt begrenzt
sie getrennt (`{ 'above-header': 1 }`); `null`, ein fehlender Schlüssel oder ein
fehlendes `maxVisible` bedeutet unbegrenzt. Die Grenze gilt für das Sichtbare:
Wird der oberste Hinweis geschlossen, rückt der nächste nach.

### Wann sie erscheinen

`startsAt` und `endsAt` werden im **Browser** gegen die Uhr des Lesers geprüft.
So beginnt und endet ein Hinweis ohne Deployment und ohne Neuladen der Seite.
Das Zeitfenster ist halboffen: sichtbar ab `startsAt` und bis `endsAt`.

Schreibe einen Offset, wenn der exakte Moment zählt
(`2026-03-01T09:00:00Z`). Ohne Zeitzone gilt die lokale Zeit des Lesers. Ein
ungültiges Datum wird wie ein fehlendes behandelt, damit ein Tippfehler den
Hinweis nicht unbemerkt verschluckt.

### Schließen

Ein geschlossener Hinweis bleibt über Navigation, Reload und Sprachwechsel
geschlossen. Gespeichert wird seine **Identität**:

- mit `id` genau diese locale-unabhängige Zeichenfolge; eine neue `id` zeigt den
  Hinweis allen erneut,
- ohne `id` ein Wert aus allen Locale-Varianten von `text` und `link`; neuer
  Wortlaut ist eine neue Ankündigung, eine andere Reihenfolge der Locale-Keys
  nicht.

Setze eine `id`, wenn du Tippfehler korrigieren willst, ohne den Hinweis erneut
anzuzeigen. Die Werte liegen nur im `localStorage` des Browsers; Einträge für
entfernte Ankündigungen werden verworfen. Weil Zeitfenster und Schließen dem
Browser gehören, wird der Streifen erst nach der Hydration gezeichnet.

Die Komponentenoberfläche steht bei
[DuxtAnnouncements](/reference/components/layout/duxt-announcements).

## Navigation und Links

Ein Sektionseintrag ist ein Link plus ein eigenes Feld:

| Feld       | Typ      | Hinweise                                                           |
| :--------- | :------- | :------------------------------------------------------------------ |
| `pageIcon` | `string` | Icon für Seiten dieser Sektion ohne eigenes. Überschreibt `duxt.pageIcon`; das Frontmatter einer Seite gewinnt weiterhin |

Nützlich, wo Seiten gar kein Icon tragen können — das Frontmatter einer ADR ist
auf `title`, `description`, `status` und `date` festgelegt, das
Entscheidungsprotokoll rendert also sonst ohne.

| Schlüssel    | Standard    | Was er steuert                                                |
| :----------- | :---------- | :------------------------------------------------------------- |
| `navigation` | ein Eintrag | Links der Navigationsleiste; ein Eintrag mit `children` wird ein Aufklappmenü |
| `sections`   | **leer**    | Die zweite Navigationszeile — die obersten Teile der Doku       |
| `links`      | **leer**    | Icon-Links rechts in der Navigationsleiste                      |
| `aside`      | nur Titel   | `title` und `links` unter dem Inhaltsverzeichnis                |
| `footer`     | **leer**    | `copyright` und `legal`                                         |
| `landing`    | eine Aktion | Die Seite unter `/` |

Die meisten davon werden leer ausgeliefert, aus zwei verschiedenen Gründen.
`links`, `aside.links`, `footer.legal`, `title`, `version` und der Text der
Landeseite benennen ein bestimmtes Projekt — ein Repository, eine Community, ein
Impressum, einen Namen, ein Release, einen Satz darüber, was es tut — und gehören
dem, der die Seite betreibt. `sections` benennt die Teile eines Dokumentations**baums**, und der
Baum ist deiner: vier Reiter, die auf von der Ebene geratene Ordnernamen zeigen,
führten ins Leere. Bis du sie setzt, rendert die Zeile nicht und die
Seitenleiste zeigt den ganzen Baum, was die richtige Form für eine Doku ist, die
nicht in Sektionen geteilt ist.

**Eine Zeile je Quelle.** Ein Eintrag gehört der Quelle, unter deren URL-Präfix
er liegt, und die Zeile zeigt die Einträge der Quelle, in der der Leser gerade
ist — eine Seite mit Dokumentation an der Wurzel und etwas anderem unter `/demo`
schreibt also eine `sections`-Liste, und jeder Bereich zeichnet seinen Teil
davon. Eine generierte Section unterhalb eines Bereichs (`/demo/openapi`) gehört zu
diesem Bereich, statt selbst einer zu sein. Eine Seite mit einer einzigen Quelle
hat einen Bereich, jeder Eintrag liegt darin, und die Zeile ist genau die Liste,
wie sie geschrieben steht.

### Landeseite

Die Seite unter `/` zeichnet drei Bänder, und jedes davon fehlt, bis sein
Schlüssel gesetzt ist.

| Schlüssel     | Standard                 | Was er zeichnet                                 |
| :------------ | :----------------------- | :---------------------------------------------- |
| `badge`       | **keins**                | Die Pille über der Überschrift — Text oder ein Abzeichen-Objekt |
| `headline`    | **keine**                | Die `h1`; fällt auf `title` zurück               |
| `description` | **keine**                | Der Absatz darunter und die Meta-Beschreibung der Seite |
| `actions`     | eine, „Doku lesen“       | Die Schaltflächen des Heldenbereichs; eine Aktion ohne `to` löst auf die erste Sektion auf |
| `command`     | **keiner**               | Ein kopierbarer Installationsbefehl unter den Schaltflächen |
| `preview`     | **keine**                | Eine Seite dieser Website, eingebettet in ein Browserfenster |
| `features`    | **leer**                 | Das Kartenraster                                 |

`command` ist eine schlichte `string`, kein Text: Ein Shell-Befehl ist in jeder
Sprache gleich, und einer, der versehentlich übersetzt wurde, ist einer, der
nicht läuft. Die Ebene liefert keinen — sie weiß nicht, wie dein Projekt heißt,
aus demselben Grund, aus dem `links` leer ist.

Es gibt bewusst keinen abschließenden Handlungsaufruf. Die Schaltflächen des
Heldenbereichs sind der Aufruf; sie unter einem Kartenraster zu wiederholen verlangt
von einem gerade angekommenen Leser, sich zweimal zu entscheiden.

`badge` ist aus demselben Grund leer wie `links`: Eine Pille über der
Überschrift sagt etwas über den Zustand eines Projekts — „beta“, „v2 ist da“ —
und die Ebene weiß nichts über den Zustand deines. Eine Zeichenkette ist die
Kurzform; das Objekt fügt Icon, Farbe und Ziel hinzu.

| `badge`-Feld | Typ       | Anmerkungen                                                  |
| :----------- | :-------- | :------------------------------------------------------------ |
| `label`      | Text      | Pflicht. `{version}` wird durch die `version` der Seite ersetzt |
| `icon`       | `string`  | Beliebiger Iconify-Name                                        |
| `variant`    | `string`  | `default`, `secondary`, `outline`, `success`, `destructive`    |
| `to`         | `string`  | Macht die ganze Pille zu einem Link                            |
| `external`   | `boolean` | Der neue Tab                                                   |

```ts [app/app.config.ts]
badge: {
  label: '{version} veröffentlicht',
  icon: 'lucide:rocket',
  variant: 'success',
  to: 'https://github.com/acme/sdk/releases/latest',
  external: true
}
```

`preview` ist ein lebendes Fenster, kein Bild. Es bettet eine Seite **dieser**
Website in einen Browserrahmen ein, den der Leser scrollen, in dem er
navigieren und dessen Theme er wechseln kann, ohne die Landeseite zu verlassen.
Der Rahmen wird erst eingehängt, wenn das Band ins Sichtfeld scrollt, und nie
beim Server-Rendering — ein iframe im ursprünglichen HTML ist ein zweiter voller
Seitenaufbau, der mit dem ersten konkurriert.

| `preview`-Feld | Typ      | Anmerkungen                                                |
| :------------- | :------- | :----------------------------------------------------------- |
| `to`           | `string` | Die einzubettende Seite; standardmäßig die erste Sektion      |
| `height`       | `string` | Beliebige CSS-Länge. Standard `32rem`, `24rem` unter `sm`     |
| `src`          | `string` | Ein Bildschirmfoto ANSTELLE der lebenden Seite                |
| `srcDark`      | `string` | Bedient den Dunkelmodus; ohne es bedient `src` beide          |
| `alt`          | Text     | Der Alt-Text des Bildes und der zugängliche Name des Rahmens  |

::callout{type="tip" title="Ein Bildschirmfoto ist die günstigere Wahl"}
Der lebende Rahmen lädt die Anwendung ein zweites Mal. Eine Seite, die das
lieber nicht zahlt, setzt `src` und bekommt dasselbe Fenster um ein Standbild.
::

### Form eines Links

| Feld          | Typ                  | Anmerkungen                                          |
| :------------ | :------------------- | :--------------------------------------------------- |
| `label`       | Text                 | Pflicht                                               |
| `to`          | `string`             | Ein Dokumentationspfad wird lokalisiert, eine URL nicht |
| `icon`        | `string`             | Beliebiger Iconify-Name, z. B. `lucide:rocket`        |
| `description` | Text                 | In einem Aufklappmenü der Navigationsleiste gezeigt   |
| `external`    | `boolean`            | Der neue Tab und der Pfeil — nie das Routing          |
| `children`    | `DuxtLink[]`         | Macht aus einem Navigationseintrag ein Aufklappmenü   |
| `variant`     | Schaltflächenvariante | Nur `landing.actions`                                |

Jedes als _Text_ markierte Feld nimmt ein Literal, einen i18n-Schlüssel oder
einen Datensatz je Locale — siehe [Lokalisierung](/concepts/localisation).

## Quellen

| Schlüssel       | Gelesen zur | Was er steuert                             |
| :-------------- | :---------- | :----------------------------------------- |
| `sources`       | Build-Zeit  | Die Dokumentationsquellen                  |
| `sourceOptions` | Build-Zeit  | Wie diese Quellen zu URL-Präfixen werden   |
| `versions`      | Laufzeit    | Überschreibt die abgeleiteten Versionen, wenn sie Beschriftungen oder Beschreibungen brauchen |

Beide Quellenschlüssel haben [eine eigene Seite](/reference/sources). `locales`
ist der dritte Build-Zeit-Schlüssel: eines der drei zu ändern erfordert einen
erneuten Build.

## Der Feed

`/rss.xml` ist leer, bis `feed.path` eine Sektion benennt:

```ts
feed: { path: '/changelog', title: 'mein Projekt — Releases' }
```

Standardmäßig aus, mit Absicht. Ein Feed ist eine Liste von Dingen, die
**passiert** sind, und eine bearbeitete Referenzseite ist kein Ereignis — eine
Seite, die jede Seitenbearbeitung als Eintrag veröffentlicht, bringt ihren
Lesern das Abbestellen bei. Einträge werden nach dem eigenen `date` einer Seite
geordnet, ersatzweise nach dem letzten Commit, der sie berührt hat, und nur die
Standardversion jeder Quelle steuert etwas bei — ein versionierter Changelog
wiederholt also nicht jeden Eintrag einmal je Version.


Die Sektion, auf die er üblicherweise zeigt, ist eine
[Release-Historie](/reference/changelog), deren Seiten je ein `date` tragen.

## Erzeugt, nicht geschrieben

| Schlüssel          | Geschrieben von | Enthält                                          |
| :----------------- | :-------------- | :----------------------------------------------- |
| `resolvedSources`  | dem Build       | Das Manifest: welche Collection welches Präfix bedient |
| `layerVersion`     | dem Build       | duxts eigene Version, für den Fußbereich          |
| `layerRepository`  | dem Build       | duxts eigenes Repository, für den Fußbereich      |

Eines der drei von Hand zu setzen wird beim nächsten Build überschrieben.