# mittwald Flow — Full documentation
> Full-text Markdown export of the Flow design system documentation, generated for LLMs. The React components are published as `@mittwald/flow-react-components`. Source: https://github.com/mittwald/flow
Individual pages are also available by prefixing any docs path with `/raw/` and appending `.md`.
---
# Installation
---
# Installation der Flow React Component Library
Die Flow React Component Library kann mit einem Package Manager wie `npm` oder
`yarn` installiert werden.
```shell
yarn add @mittwald/flow-react-components
```
Zur leichteren Verwaltung der Abhängigkeiten werden alle Components innerhalb
diese Packages bereitgestellt.
---
# Styles importieren
Um die Components-Styles zu verwenden, musst du das Stylesheet importieren. Füge
diese Zeile zum Einstiegspunkt deines Projektes hinzu.
```
import "@mittwald/flow-react-components/all.css";
```
---
# Defaults für Components festlegen
Einige Components verhalten sich nach einem Standard, den du für deine gesamte
Anwendung festlegen kannst, anstatt ihn an jeder Verwendung zu wiederholen.
Dafür umschließt du deine Anwendung mit dem ``:
```tsx
```
Jede einzelne Verwendung bleibt stärker als der Standard: Setzt eine
[List](https://flow.mittwald.de/components/list/list) das `disableInitialSuspenseBoundary`-Property an
ihrer Datenquelle oder eine [Form](https://flow.mittwald.de/components/react-hook-form/form) das
`confirmModalCloseOnUnsavedChanges`-Property, gilt dort dieser Wert.
Verschachtelte Provider gelten jeweils für ihren Teilbaum und übernehmen die
Werte, die sie nicht selbst nennen.
Diese Defaults stehen zur Verfügung:
| Default | Bedeutung | Wert ohne Provider |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------ |
| `List.disableInitialSuspenseBoundary` | Die List rendert beim initialen Laden keine eigene Suspense-Boundary, sondern erscheint erst mit geladenen Daten. | `false` |
| `Form.confirmModalCloseOnUnsavedChanges` | Das Schließen eines Modals, das eine Form mit ungespeicherten Änderungen enthält, muss bestätigt werden. | `true` |
---
# Aktualisieren
Ein Befehl hebt alle Flow-Abhängigkeiten auf die Zielversion, installiert und
führt den Codemod jeder Migration bis zu dieser Version aus:
```shell
npx @mittwald/flow-codemods@latest upgrade
```
Er ersetzt den Migrationsleitfaden nicht: die meisten Änderungen haben keinen
Codemod. Was in deinem Bereich von Hand ansteht, listet der Befehl am Ende auf –
vorab und ohne etwas zu verändern zeigt es `list`.
Welche Änderung ab welcher Version greift und wie Deprecation-Warnungen den
Vorlauf bilden, steht unter [Upgrades](https://flow.mittwald.de/get-started/upgrades). Was Flow dir
innerhalb einer Linie zusichert, steht unter
[Versionierung & Stabilität](https://flow.mittwald.de/get-started/versioning).
---
# Mit KI-Agents arbeiten
Ein Coding-Agent liest von sich aus nur die Anweisungen deines Projekts. Diese
Dokumentation und die Hinweise im Paket findet er nur, wenn sie dort genannt
sind. Füge deshalb diesen Block in die `AGENTS.md` (oder `CLAUDE.md`) deines
Projekts ein:
```markdown
## UI mit Flow
Bevor du UI mit Flow baust oder änderst:
1. Lies die USAGE.md des Flow-Pakets, das in package.json steht:
- `@mittwald/flow-react-components` (Anwendung):
node_modules/@mittwald/flow-react-components/USAGE.md
- `@mittwald/flow-remote-react-components` (mStudio-Extension):
node_modules/@mittwald/flow-remote-react-components/USAGE.md. Importiere
dann immer aus diesem Paket, auch wenn die Beispiele der Dokumentation
`@mittwald/flow-react-components` zeigen.
- `@mittwald/flow-remote-vue-components` (mStudio-Extension in Vue, Beta):
node_modules/@mittwald/flow-remote-vue-components/USAGE.md.
2. Lies https://flow.mittwald.de/llms.txt und öffne die dort verlinkte Seite,
bevor du eine Component, ein Layout oder einen UI-Text festlegst. Antworte
nicht aus dem Gedächtnis.
3. Baust du eine neue App, Seite oder einen Ablauf, wähle zuerst das passende
Template aus dem Abschnitt „Templates“ in llms.txt: erst die App-Shell, dann
das Seiten-Template, dann Overlays und Bausteine. Starte vom Beispielcode des
Templates. Weiche nur davon ab, wenn ausdrücklich ein anderes Layout
gewünscht ist – auch bei schnellen Prototypen.
```
Die `USAGE.md` passt zur installierten Version, `llms.txt` beschreibt den
aktuellen Stand der Dokumentation und verlinkt jede Seite als Markdown. Auf
jeder Seite dieser Dokumentation führt außerdem „Markdowndatei öffnen“ zur
Markdown-Fassung.
---
# mStudio-Extensions mit Vue (Beta)
Eine mStudio-Extension kann statt in React auch in Vue geschrieben werden. Das
Package `@mittwald/flow-remote-vue-components` ist das Vue-Gegenstück zu
`@mittwald/flow-remote-react-components`: Die Extension rendert damit dieselben
Flow Components im mStudio.
```shell
FLOW=$(npm view @mittwald/flow-remote-vue-components@next version)
yarn add @mittwald/flow-remote-vue-components@$FLOW @mittwald/flow-react-components@$FLOW @mittwald/ext-bridge@$FLOW vue react react-dom
```
`@mittwald/flow-react-components` ist eine Laufzeit- und Typ-Abhängigkeit: Die
Remote-Verbindung serialisiert Passwort-Policies über seinen Einstiegspunkt
`mittwald-password-tools-js`, der `react` importiert, und die Prop-Typen der
Components stammen aus seinen Deklarationen. Fehlt das Package oder `react`,
schlägt der Build der Extension fehl, ebenso ohne `@mittwald/ext-bridge`, das
`RemoteRoot` importiert. `react-dom` ist eine Peer-Dependency von
`@mittwald/flow-react-components`. `yarn` installiert Peer-Dependencies nicht
selbst, deshalb stehen alle in der Zeile. Alle Flow-Packages brauchen genau die
Version von `@mittwald/flow-remote-vue-components`. Ohne Versionsangabe nehmen
pnpm und yarn das `latest`-Tag der beiden anderen, und das ist eine andere
Release-Linie.
Das Package ist **Beta**. Es ist von der Breaking-Change-Zusage ausgenommen,
seine API kann sich auch außerhalb einer Major-Version ändern (siehe
[Component Lifecycle](https://flow.mittwald.de/get-started/versioning#component-lifecycle)). Welche
Components und Funktionen es schon abdeckt und welche noch fehlen, beschreibt
seine
[USAGE.md](https://github.com/mittwald/flow/blob/main/packages/remote-vue-components/USAGE.md).
---
# Stylesheet
Die Flow-Components-Bibliotheken werden über ein gemeinsames Stylesheet, das
auch für sich alleine verwendet werden kann, gestyled. Das kann nützlich sein,
wenn du dein eigenes Framework verwendest, um Components zu bauen, und dich
dabei an die mittwald Styling Guidelines halten willst.
Auf dieser Seite findest du alle Informationen darüber, wie du das Stylesheet
installieren kannst und wie die Klassennamen strukturiert sind.
---
# Installation des Standalone Stylesheets
Die Flow-Stylesheet-Bibliothek wird über NPM veröffentlicht und kann mit einem
Package Manager wie `npm` oder `yarn` installiert werden.
```shell
yarn add @mittwald/flow-stylesheet
```
---
# Styles importieren
Um die Components-Styles zu verwenden, musst du das Stylesheet importieren. Füge
diese Zeile zum Einstiegspunkt deines Projektes hinzu.
```
import "@mittwald/flow-stylesheet/css";
```
---
# Layered-Variante (optional)
Das Standard-Stylesheet (`@mittwald/flow-stylesheet/css`) ist **ungelayert**:
welche Regel gewinnt, entscheidet sich über Spezifität und Quell-Reihenfolge.
Optional gibt es eine **gelayerte Variante**, deren Styles in
[CSS Cascade Layers](https://developer.mozilla.org/de/docs/Web/CSS/@layer)
organisiert sind:
```
import "@mittwald/flow-stylesheet/css-layered";
```
Aus dem `@mittwald/flow-react-components`-Paket entsprechend:
```
import "@mittwald/flow-react-components/all-layered.css";
```
Sie liegt unter einem `flow`-Layer mit dieser Reihenfolge:
```css
@layer flow.tokens, flow.reset, flow.base, flow.components;
```
- `flow.tokens` – die Design-Token als CSS Custom Properties
- `flow.reset` – der globale Reset
- `flow.base` – Schriftarten (`@font-face`) und globale Basis-Styles
- `flow.components` – die Styles der einzelnen Komponenten
---
## Warum die gelayerte Variante?
Nach den Regeln der Cascade Layers gewinnt **ungelayertes** CSS immer gegen
gelayertes – unabhängig von der Spezifität. Mit der gelayerten Variante
überschreibst du Flows Styles daher ohne `!important` oder Spezifitäts-Tricks:
```css
/* Gewinnt gegen Flows .flow--button, ohne !important */
.flow--button {
border-radius: 0;
}
```
Wenn du selbst mit Cascade Layers arbeitest (z. B. Tailwind), deklariere deine
Layer **nach** `flow`, damit sie Vorrang behalten.
---
## Welche Variante wann?
- **Standard (`css` / `all.css`):** klassisches, spezifitätsbasiertes Verhalten.
Passt auch für Apps mit einem aggressiven globalen Reset (z. B.
`* { all: initial }`), der gelayerte Styles sonst vollständig überschreiben
würde.
- **Layered (`css-layered` / `all-layered.css`):** einfache, spezifitätsfreie
Überschreibbarkeit und saubere Interop mit eigenen Cascade Layers.
---
# Anwendung des Stylesheets
Um anfangen zu können, solltest du verstehen, wie die Klassennamen strukturiert
sind. Die im Stylesheet bereitgestellten Klassennamen folgen einem konsistenten,
komponentenbasierten und leicht zu verstehendem Schema.
---
## Generelle Klassennamen-Auszeichnungen
Alle Klassennamen sind in Lowercase geschrieben und benutzen `-` um Wörter zu
trennen und `--` um logische Abschnitte zu unterteilen.
Der erste logische Abschnitt ist immer der `flow` Namespace. Andere Abschnitte
könnten beispielsweise so aussehen:
### Components
```css
.flow--button
.flow--heading
.flow--alert-badge
```
### Sub-Components
```css
.flow--navigation
.flow--navigation--navigation-group
```
### Spezialisierung: Verschiedene Varianten
```css
.flow--button--primary
.flow--alert--danger
.flow--icon--size-s
```
### Spezialisierung: In einer Komposition verwendete Components
```css
.flow--button--icon
.flow--alert--heading
```
---
## Ein Hinweis zur Spezialisierung
Klassennamen die verwendet werden, um die Basiskomponente zu spezialisieren,
müssen immer **zusätzlich zum Basis-Klassennamen** verwendet werden.
Hier ein paar Beispiele um die diese Anforderung zu verdeutlichen:
```tsx
```
### Kombinierte Varianten
```tsx
```
### In einer Komposition verwendete Components
Es ist gängige Praxis, größere Components aus bereits bestehenden kleineren
Components zusammenzusetzen. Der [Alert](https://flow.mittwald.de/components/status/alert) besteht
beispielsweise aus einem [Icon](https://flow.mittwald.de/components/content/icon), einer
[Heading](https://flow.mittwald.de/components/content/heading) und optionalem Inhalt. Die verwendeten
Components müssen ihren Basis-Klassennamen für das grundsätzliche Styling
erhalten (`flow--heading`), sowie den spezialisierten Klassennamen
(`flow--alert--heading`), um spezifische Styles des Inline Alerts zu erhalten.
```tsx
import ExampleSvg from "@/content/get-started/stylesheet/examples/components/ExampleSvg";
```
---
# Versionierung & Stabilität
---
# Semantic Versioning
Alle `@mittwald/flow-*`-Packages teilen sich eine gemeinsame Version. Dieselbe
Zusage gilt damit einheitlich für jedes Package. Es gibt keine
Package-spezifischen Sonderregeln – ausgenommen ein Package, das als Ganzes Beta
ist (siehe [Component Lifecycle](#component-lifecycle)).
Seit Version `1.0.0` ist aus einer guten Absicht eine verlässliche Zusage
geworden: Wir halten uns an [Semantic Versioning](https://semver.org) und gehen
Änderungen planbar und nachvollziehbar an.
- **Major** – enthält Breaking Changes. Ein Update kann Anpassungen in deinem
Code erfordern. Major-Versionen bleiben bewusst selten.
- **Minor** – fügt neue Funktionen hinzu und ist abwärtskompatibel.
- **Patch** – enthält Bugfixes und ist abwärtskompatibel.
Entscheidend ist die Grenze: Welche Änderung erzwingt eine neue Major-Version,
und welche darf in einem Minor oder Patch erscheinen? Die folgenden Abschnitte
beschreiben diese Grenze aus Sicht der Entwickler. Wie du ein Update oder eine
Migration konkret durchführst, beschreibt [Upgrades](https://flow.mittwald.de/get-started/upgrades).
---
# Worauf du dich verlassen kannst
Diese Bereiche sind durch die Versionierung geschützt. Eine inkompatible
Änderung daran erscheint nur in einer neuen Major-Version:
- **Runtime Public API von `public.ts`** – welche Components und Exports
existieren und welche Props sie zur Laufzeit akzeptieren, sowie das
dokumentierte Verhalten dokumentierter Funktionen. Die Type-Ebene ist hiervon
ausdrücklich ausgenommen (siehe unten).
- **Components aus `@mittwald/flow-remote-react-components`** – die in
mStudio-Extensions verwendete API. Ihre Props sind der Vertrag mit
Extension-Entwicklern und unterliegen derselben Zusage.
- **Veröffentlichte Icons** – ein Icon zu entfernen oder umzubenennen ist ein
Breaking Change. Icons werden ohnehin nie entfernt, sondern nur deprecated.
- **Das Remote-Protokoll** – die versionierte Verbindungsschicht zwischen
Extension und Host bleibt kompatibel, solange dies möglich ist.
Wenn ein Teil der öffentlichen API entfernt werden soll, wird er zunächst als
deprecated gekennzeichnet und nicht sofort entfernt. Der alte Pfad bleibt
erhalten und wird zur Laufzeit über eine Deprecation-Warnung angekündigt, bevor
er in einer Major-Version entfernt wird. Das betrifft die gesamte öffentliche
API, nicht nur die in Extensions verwendeten Components.
---
# Node- und React-Unterstützung
Node und React sind echte Runtime-Voraussetzungen. Auch hier gibt es klare
Regeln, welche Änderung eine neue Major-Version erzwingt.
**Node**
- Der garantierte Node-Floor ist die aktiv unterstützte Node-LTS, aktuell
`node >=24`, einheitlich über alle Packages. Er wird nur bei konkretem Bedarf
angehoben, nicht um neuen Releases hinterherzulaufen.
- Eine Node-Version fallen zu lassen, die noch in ihrem LTS-/Maintenance-Fenster
liegt, ist ein Breaking Change (→ Major). Eine bereits End-of-Life-Version
fallen zu lassen, darf in einem Minor erscheinen.
- Strenger sind die Node-Runtime-Packages `@mittwald/ext-bridge` und
`@mittwald/flow-remote-core`: Für sie ist jedes Anheben des Node-Floors ein
Breaking Change (→ Major), unabhängig von EOL, und ihr Floor kann
konservativer sein als der der übrigen Packages.
**React**
- Das **Erweitern** der akzeptierten Range (z. B. `^19` → `^19 || ^20`) ist
abwärtskompatibel (Minor).
- Das **Anheben der Mindestversion** oder das Fallenlassen einer React-Major ist
ein Breaking Change (→ Major).
---
# Was nicht garantiert ist
Damit sich das Design System weiterentwickeln kann, sind die folgenden Bereiche
bewusst nicht durch die Versionierung abgedeckt. Sie können sich in jedem
Release ändern, auch in einem Minor oder Patch.
## Best-effort
TypeScript-Typen, CSS-Klassennamen und Design Tokens fallen nicht unter Semantic
Versioning. Eine Änderung daran ist unwahrscheinlich – wir nehmen sie nicht
leichtfertig vor –, aber möglich: Sie kann in jedem Release erscheinen, auch in
einem Patch, ohne Major-Version und ohne Deprecation-Phase. Wenn du dich auf
einen davon stützt, lies „So schützt du dich“.
- **TypeScript-Typen.** Das gilt für die gesamte Type-Ebene: Auch das Entfernen
oder Umbenennen eines exportierten Typs oder das Verengen eines Prop-Typs ist
für sich genommen kein Breaking Change. Nennenswerte Type-Änderungen werden
dennoch im Changelog erwähnt.
- **CSS-Klassennamen.** Ein internes Implementierungsdetail – weder ihre
Existenz noch ihre Schreibweise ist zugesagt.
- **Namen und Werte von Design Tokens.** Ein Token kann umbenannt werden, einen
anderen Wert bekommen oder verschwinden.
## Ebenfalls nicht abgedeckt
- **Das visuelle Erscheinungsbild.**
- **Die interne DOM-Struktur.**
---
# So schützt du dich
Weil die best-effort-Bereiche bewusst nicht garantiert sind, gibt es drei
Regeln, an die du dich als Entwickler halten solltest.
Drei Regeln für den stabilen Betrieb
Style nicht gegen interne CSS-Klassen, referenziere Design Tokens nicht
ungeprüft über Releases hinweg, und behandle Flows TypeScript-Typen als
best-effort. Alle drei Bereiche ändern sich selten – aber wenn, dann ohne
Vorwarnung in einem beliebigen Release.
## Style nicht gegen interne CSS-Klassen
Die CSS-Klassennamen der Components sind ein internes Implementierungsdetail und
können sich in jedem Release ändern. Verlasse dich für eigenes Styling nicht
darauf, dass eine bestimmte interne Klasse existiert oder gleich heißt.
Andernfalls kann bereits ein Patch dein Styling brechen.
## Behandle Design Tokens als best-effort
Design Tokens sind für die Verwendung innerhalb von Flow gedacht. Greifst du in
eigenem Code auf eine Token-CSS-Variable zu, prüfe sie nach einem Update: Name
und Wert können sich auch in einem Patch ändern, und ein nicht mehr
existierender Token fällt still auf den Browser-Default zurück statt einen
Fehler zu werfen.
## Behandle TypeScript-Typen als best-effort
Flows TypeScript-Typen folgen nicht Semantic Versioning. Ein `tsc`-Fehler kann
daher theoretisch schon in einem Patch auftreten. Wenn ein solcher Bruch für
dein Projekt teuer wäre, pinne exakte Versionen (statt `^`-Ranges), sodass ein
Update bewusst und kontrolliert erfolgt. Zu einem bewusst herbeigeführten
Type-Breaking-Change gehört eine Migrationsnotiz in den Release Notes – so
bekommst du einen konkreten Upgrade-Pfad, auch wenn die Änderung in einem Minor
oder Patch erscheint.
---
# Component Lifecycle
Der Vertrag gilt nicht für jede Component gleich. Jede Component hat einen
Lifecycle-Status, der die obigen Regeln überschreibt:
- **Beta** – von der Breaking-Change-Zusage ausgenommen. Die API kann sich auch
außerhalb einer Major-Version ändern.
- **Stable** (Standard) – vollständig durch die obigen Regeln gebunden.
- **Deprecated** – bis zur Entfernung in einer Major-Version weiter abgesichert
und mit einem Migrationspfad versehen.
Auch ein ganzes Package kann Beta sein. Das gilt derzeit für
`@mittwald/flow-remote-vue-components`, die Vue-Anbindung für
mStudio-Extensions: Es teilt die gemeinsame Version, ist aber von der
Breaking-Change-Zusage ausgenommen. Die Beta endet, sobald jede seiner bekannten
Lücken geschlossen oder als dauerhaft vermerkt ist und seine API feststeht. Ab
dann gilt die Zusage auch für dieses Package.
Das vollständige Lifecycle-Modell beschreibt
[ADR 0003](https://github.com/mittwald/flow/blob/main/docs/adr/0003-component-lifecycle-status.md),
Beta für ein ganzes Package
[ADR 0007](https://github.com/mittwald/flow/blob/main/docs/adr/0007-beta-packages.md).
---
# Upgrades
---
# Migration bei einer Breaking Change
Jede Änderung, die eine Anpassung in deinem Code erfordert, steht mit
Vorher-Nachher-Beispiel im Migrationsleitfaden des betroffenen Packages:
- [Migrationsleitfaden @mittwald/flow-react-components](https://github.com/mittwald/flow/blob/main/packages/components/MIGRATION.md)
– deckt auch `@mittwald/flow-remote-react-components` ab
- [Migrationsleitfaden @mittwald/ext-bridge](https://github.com/mittwald/flow/blob/main/packages/ext-bridge/MIGRATION.md)
Die Einträge sind nach Version absteigend sortiert und nennen jeweils die
Version, ab der die Änderung greift. Suche die Version, von der du kommst, und
arbeite dich nach oben durch. Was dabei überhaupt als Breaking Change zählt und
eine neue Major-Version erzwingt, beschreibt
[Versionierung & Stabilität](https://flow.mittwald.de/get-started/versioning).
## Nutze die Codemod-CLI
Ein Befehl hebt alle Flow-Abhängigkeiten auf die Zielversion, installiert und
führt den Codemod jeder Migration bis zu dieser Version aus:
```shell
npx @mittwald/flow-codemods@latest upgrade
```
Ohne Argument geht es auf die nächste Minor innerhalb deiner Major.
`upgrade patch` bleibt auf deiner Minor, `upgrade major` überquert eine
Major-Grenze, und eine exakte Version oder ein Dist-Tag (`next`) gehen genau
dorthin.
Der Befehl ändert Dateien direkt und bricht auf einem unsauberen Git-Stand ab.
Der Befehl kennt keine untere Grenze: er listet auch Migrationen, die vor deiner
aktuellen Version erschienen sind. Nichts hält fest, welche davon dein Projekt
schon durchgeführt hat – und ein zweiter Durchlauf eines Codemods ändert nichts.
Ein Codemod, der durchgelaufen ist, ist allerdings kein Beweis: jeder Codemod
ist bewusst eng gefasst und lässt liegen, was er aus dem Code nicht sicher
entscheiden kann – ein gespreadetes Prop, eine Component, die über einen eigenen
Wrapper re-exportiert wird, ein dynamischer Zugriff. Einen Fehler meldet er
dafür nicht. Prüfe nach einem Upgrade deshalb auch die Migrationen mit Codemod
am Code nach, nicht nur die ohne.
Er ersetzt den Migrationsleitfaden nicht: die meisten Einträge haben keinen
Codemod. Welche das für deinen Bereich sind, listet der Befehl am Ende auf –
oder vorab, ohne etwas zu verändern, mit `list` und derselben Revision:
```shell
npx @mittwald/flow-codemods@latest list minor
```
`list` ohne Argument zeigt den gesamten Katalog, offline. Mit einer Revision
liest es dieselbe Version aus deinem `package.json` und zeigt genau den Bereich,
den `upgrade` mit dieser Revision anfassen würde – ein echter Trockenlauf.
Einen einzelnen Codemod führst du über seine ID aus:
```shell
npx @mittwald/flow-codemods@latest align-to-combine src
```
## Lass einen Agenten das Upgrade machen
Was die CLI offen lässt, kann ein Coding-Agent übernehmen: die Migrationen ohne
Codemod – das sind die meisten – und die Stellen, die ein Codemod übersprungen
hat. `list --json` liefert ihm dafür pro Migration das Feld `apply`: die
Anweisung, was zu ändern ist, knapp genug für einen Katalog und präzise genug
zum Ausführen.
Kopiere den folgenden Prompt in deinen Agenten. Er sucht zuerst, wo Flow
überhaupt deklariert ist – in einem Monorepo ist das meist mehr als ein Package,
und `list` wie `upgrade` lesen immer nur die `package.json` des Verzeichnisses,
in dem sie laufen. Dann klärt er auf, welche Zielversionen zur Wahl stehen und
was jede davon an Arbeit bedeutet, und fragt dich, welche es sein soll. Danach
geht er jede Migration im Bereich am Code nach – auch die mit Codemod – und
ändert nur, wo er das alte Muster wirklich findet.
Was dabei offen bleibt, gibt er dir nicht als Hausaufgabe zurück. Genau die
Punkte, die eine Entscheidung brauchen, sind die, bei denen er den Diff gelesen
hat und du nicht – deshalb legt er sie dir gesammelt vor, jeweils mit den
Optionen und einer Empfehlung, setzt deine Antwort um und lässt die Checks
erneut laufen. Das wiederholt sich, bis nichts mehr offen ist. Er hört vorher
nur auf, wenn etwas wirklich nicht hier entschieden werden kann – eine
Design-Entscheidung, eine Freigabe, ein Zugang – und sagt dann, worauf es wartet
und wer es tun muss.
Der Prompt ist englisch, weil alles, worauf er zeigt, englisch ist: die
CLI-Ausgabe, die `apply`-Felder und der Migrationsleitfaden.
```text
Upgrade this project's mittwald Flow dependencies.
First find out where Flow is declared. Search every `package.json` in the
project for a `@mittwald/flow-` dependency, but only the ones the project
actually maintains. `git ls-files "*package.json"` gets that right in one step:
`node_modules` and build output — `dist`, `build`, `.next`, `.output`, `.nuxt`,
`.svelte-kit`, `.turbo`, `out`, `coverage`, whatever this project emits — are
git-ignored, and a copied or generated manifest in there would have you
upgrading an artefact. Outside a git checkout, exclude those paths yourself.
In a monorepo Flow is usually declared in several workspace packages, and the
root often declares none. Every command below reads and writes exactly one
`package.json`: the one in the directory you run it from. Neither `list` nor
`upgrade` walks a workspace, so a single run at the root silently leaves every
other package behind. Tell me which packages you found, and which Flow version
each is currently on.
Then plan — change nothing yet. From one of those packages, run all three and
read the JSON:
- `npx @mittwald/flow-codemods@latest list patch --json`
- `npx @mittwald/flow-codemods@latest list minor --json`
- `npx @mittwald/flow-codemods@latest list major --json`
Each run reports the version it would resolve to (`range.target`) and every
migration in that range. A relative revision only resolves if such a release
exists: from a prerelease, or from the newest version on a line, `patch` and
`minor` exit non-zero with no JSON and a message saying so. That is not an
error to work around — it means the choice is narrower than three options.
So show me what actually resolved, side by side: target version, how many
migrations carry a codemod, how many need a hand. If fewer than three resolved,
say which and why, and put a dist-tag (`next`) or an exact version (`1.4.0`) on
the table as an equal option rather than a fallback — those always resolve.
Then ask me which target to take, and wait for my answer.
Then, on the target I chose:
- Commit or stash what is open first. Do not pass `--allow-dirty` — that guard
is what keeps a bad run `git checkout`-able.
- Run `npx @mittwald/flow-codemods@latest upgrade `. It bumps the
dependencies, installs, and runs the codemod of every migration up to the
target. Pass `--path ` unless the sources really are under `./src` —
find that directory before the first command, not after.
- It bumps every package published from the Flow monorepo, not only the
`@mittwald/flow-*` ones: `@mittwald/ext-bridge`,
`@mittwald/mstudio-ext-react-components` and `@mittwald/react-tunnel` are on
the same release train and move too. Tell me which ones it actually rewrote.
- Only if more than one package declares Flow: run it once per package, from
that package's own directory, passing the resolved exact version rather than
`patch`/`minor`/`major` — a relative revision resolves from the version that
one package is on, so packages that started out apart would land apart. And
commit after each one: the dirty-tree guard reads `git status` for the whole
repository, so the second run would otherwise refuse. Do not reach for
`--allow-dirty` instead; committing keeps each package a revertible step.
- `peerDependencies` are reported and deliberately left untouched. Note what it
named as an open decision for the closing round below — do not hand it to me
now and do not narrow a range on your own. That report comes after the
install, so a failed install means you never see it; read the manifests
yourself then.
- If the install fails with `ETARGET` or `ERR_PNPM_NO_MATCHING_VERSION` on a
Flow package, that is the publishing window right after a release. Wait a few
minutes and install again — do not pin anything and do not mix Flow versions
to get around it.
- If the install fails for any other reason, do not wait and retry — that only
helps the case above. The manifest is already rewritten while `node_modules`
still holds the old version, so read the actual error and finish the install
yourself with the project's own package manager. The command detects the
manager from lockfiles in the directory it runs in, which in a workspace
package is the wrong place to look: check the repository root for a lockfile
and a `packageManager` field before trusting what it chose. Confirm the
versions it wrote are published, then install; revert the bump only if they
are not.
Then check every migration against the code — every entry in the JSON, including
the ones with `action: "codemod"`. A codemod is deliberately scoped
narrowly and skips anything it cannot decide from the source: a spread prop, a
component re-exported through a local wrapper, a dynamic access, a file outside
the path it ran on. It reports no error for those. A codemod that ran clean is
therefore not proof the old pattern is gone — treat its run as a head start, not
as a result. Read the `upgrade` report for what it admits: an entry that changed
no file, declined every file it looked at, or found nothing to process is the
strongest hint that its pattern is still there in a shape it could not handle.
One caveat on that report. "No files were processed. Is the path right?" is also
what a codemod that crashed before touching a file looks like — the counters are
identical, and the summary line guesses at the path. Check stderr for a stack
trace before you believe the path is wrong, and tell me if a codemod died: its
migration then had no run at all, and everything below is the only thing that
covers it.
Include the entries flagged `catchUp: true`. They shipped before the version
this project was on and may never have been done here.
For each entry:
1. Read `apply`. It is the whole instruction — the JSON carries no example, and
the human-readable `list` output has none either. For a before/after example,
look up the entry id in `MIGRATION.md`, which ships inside
`@mittwald/flow-react-components` and is also on GitHub. Do not paste the
command `list` prints beside an entry: its path argument is hardcoded to
`src`, which is wrong wherever `--path` was needed above.
2. Search for the old pattern across every package that uses Flow, not just the
one you last ran in. Resolve Flow imports properly — named, aliased and
namespace imports all count — and treat an entry marked `remotePackage: true`
as applying to `@mittwald/flow-remote-react-components` too.
3. No match: say so and move on. No edit.
4. Match: make the change `apply` describes, by hand. On a codemod entry, do not
just run the codemod again — it already saw that site and declined it, so a
second run declines it again.
5. Leave an edit undone only when the right migration is genuinely ambiguous
from the code. Do not hand it to me here: record it as an open decision with
the entry id, the site, the options you see and which one you would pick and
why. The closing round below settles it.
Keep the change scoped to the upgrade. No unrelated refactoring, no reformatting
of files you did not otherwise touch.
Then run the project's own checks — type check, lint, build, tests, whatever
`package.json` provides — and fix what the upgrade broke. In a monorepo run them
at the root so a package you did not touch is covered too: a Flow change in one
package can break its consumer in another.
A failure that was already there before the upgrade is not yours to fix, and not
mine to be handed either. Check it against the commit you started from, then
say which of the two it is. Pre-existing ones: name them in one line each and
leave them alone — chasing them turns an upgrade into an open-ended cleanup. If
the project has no checks at all, say that too, so neither of us mistakes
silence for a green run.
Then finish the migration with me. Do not hand me a list of leftovers — a
migration that ends in homework is not done, and the open items are exactly the
ones where you have the context and I do not, having not read the diff.
Go through every open decision you recorded, plus the `peerDependencies` report,
one at a time. For each: name the entry and the site, show the code as it
stands, lay out the options in a sentence each, say which one you would pick and
why, and ask me. Ask them in one pass rather than trickling them out, and put
the ones that block a check first. If a decision turns out to have one obviously
right answer once you look properly, take it and tell me — do not spend my
attention on a question you can answer.
Apply each answer as I give it, then run the checks again: a decision can break
something the previous pass did not touch, and closing one item can make another
moot. Repeat until nothing is open.
Stop only when one of these is true, and say which:
- Nothing is open. Every check that passed before the upgrade passes again — or
the project has none — and no decision is waiting.
- What is left genuinely cannot be settled here — it needs someone who owns the
design, an approval, a credential, or a change outside this upgrade's scope.
Name each one, say exactly what it is waiting on and who has to do it, and put
it in a state I can pick up: the code compiling, or a single clearly marked
spot to come back to.
- I told you to stop.
Then summarize: which packages you upgraded and from which version to which,
which codemods changed files, which entries you fixed by hand, which found
nothing, what we decided together — and whether anything is still open, with the
reason from the list above. If nothing is open, say so plainly rather than
padding the summary with caveats.
Finally, do not leave the whole migration sitting uncommitted. Walk me through
the diff at the level of what changed and why — not file by file — then propose
how to commit it: one commit, or one per package in a monorepo, with the
messages written out. Commit once I say so. If I would rather review first,
leave the tree as it is and say so in one line; do not commit without an answer,
and do not push in any case.
```
Zwei Dinge, die der Prompt bewusst nicht tut: Er lässt den Agenten nicht auf
einem unsauberen Git-Stand starten, damit ein missglückter Lauf ein
`git checkout` bleibt. Und er lässt ihn nichts an deinen `peerDependencies`
ändern – was deine eigenen Consumer installieren dürfen, ist deine Entscheidung.
### Optional: erzähl uns, was nicht getragen hat
Ganz unabhängig vom Upgrade: der folgende Prompt ist ein **zweiter, eigener
Baustein**. Du kopierst ihn – wenn du magst – direkt nach dem Upgrade in
dieselbe Session. Der Agent hat den Lauf dann noch im Kontext und schreibt
daraus einen Bericht auf zwei Ebenen. Nicht kopieren ist die Absage; am Upgrade
ändert das nichts.
**Der Prompt selbst.** Ein Schritt in der falschen Reihenfolge. Eine Annahme,
die auf dein Projekt nicht zutrifft. Eine Stelle, an der der Agent raten musste
oder zwei Anweisungen sich widersprachen. Eine Situation, zu der der Prompt
schweigt. Eine Anweisung, die Arbeit gekostet und nichts geändert hat.
**Die einzelne Migration.** Ein `apply`, das ohne Raten nicht ausführbar war.
Eine Stelle, die ein Codemod liegen gelassen hat, obwohl er sie hätte
entscheiden können. Eine Migration ohne Codemod, die mechanisch entscheidbar
wäre. Ein `apply`, das eine Umbenennung beschreibt, obwohl sich auch die
Signatur geändert hat. Ein Codemod, der abgestürzt ist. Und der wertvollste
Fall: ein Bruch, den deine Checks gefunden haben und für den es gar keinen
Katalogeintrag gibt.
Beide Ebenen sehen wir sonst nicht. Ob eine Migration verständlich beschrieben
ist und ob der Upgrade-Prompt trägt, zeigt sich erst an einer echten Codebase –
nicht an unserer. Ein Bericht als
[Issue](https://github.com/mittwald/flow/issues) hilft uns deshalb wirklich
weiter: er fließt in den Katalog und in den Prompt zurück, und das nächste
Upgrade ist für alle etwas weniger Handarbeit. Ein Paste genügt, und eine
Antwort schuldest du niemandem.
Der Agent reicht ihn nicht selbst ein. Er legt ihn dir vor und sagt dir, was du
damit tun kannst – du entscheidest, was rausgeht. Code-Beispiele reduziert er
auf die kleinste anonymisierte Form, aber prüfen musst du das selbst.
```text
Write a report on that upgrade for Flow's maintainers — not for me. Report where
their tooling fell short, not what my project needed. Judge every point from
what actually happened in this session, not from reading any text back: a
plausible-sounding critique nobody hit is worse than no report, because it sends
them after a problem that does not exist and nothing here can be checked against
anything.
It has two parts, and the first is the one nobody ever sends.
Part one, the upgrade prompt you just followed. You ran it end to end against a
codebase its authors have never seen, which is the only way its gaps show up at
all. Report:
- A step that came in the wrong order, or that assumed something this project
does not have.
- A point where you had to guess what was meant, or where two instructions
pulled in different directions.
- A situation you ran into that the prompt says nothing about, and what you did
instead.
- An instruction that turned out to be unnecessary, or that cost work without
changing the outcome.
- What you would have wanted to know before you started.
Part two, the migration catalogue. Name the entry id for each:
- Entries whose `apply` you could not execute without guessing. Quote the field
and say what it left open.
- Sites a codemod declined that it looks like it could have handled. Name the
shape that defeated it.
- Entries with `action: "manual"` whose change looks mechanically decidable — a
codemod could exist there and does not.
- Breaks the checks surfaced that no entry in the catalogue covers at all. These
matter most: nothing else reveals them.
- An `apply` that described a rename but was also a type or behaviour change, or
that contradicted the code you actually saw.
- A codemod that crashed, or a command the tool printed that did not work as
printed.
Write it as Markdown that can go into an issue unedited, opening with the
version you went from and to, the package manager, and whether this is a
monorepo. Reduce every code sample to the smallest anonymised shape that still
shows the problem:
no project or package names, no file paths, no business logic. If a point has
nothing under it, drop the point rather than padding it; if a whole part would
be empty, say so in one line.
Do not file it anywhere and do not push it. Hand it over, and close by telling
me what I can do with it:
- Send it as a new issue at https://github.com/mittwald/flow/issues. This is
genuinely valuable to the Flow team — a run against a codebase they cannot see
is the only way they learn where the catalogue and that prompt fall short, and
what comes back feeds into both. It takes one paste.
- Or drop it. Nothing depends on it and no answer is owed.
Either way the choice is mine, and so is the review: tell me to read it for
anything about my project I would not put in a public tracker before I send it.
```
## Deprecation-Warnungen sind der Vorlauf
Wird ein Pfad deprecated, bleibt er funktionsfähig und meldet sich zur Laufzeit
per `console.warn`. Diese Warnungen sind die Vorwarnzeit vor der nächsten
Major-Version – behandle sie als Aufgabenliste, nicht als Rauschen. Um sie
zentral einzusammeln, etwa im Error-Tracking, umschließe deine Anwendung mit
einem `DeprecationWarningProvider` und gib ihm einen `onWarning`-Handler:
```tsx
reportToTracking(message)}>
```
---
# Direkt nach einem Release
Ein Release veröffentlicht die Packages **nacheinander**, nicht gleichzeitig.
Für einige Minuten liegt in der npm-Registry deshalb für die schon
veröffentlichten Packages die neue Version, für die restlichen noch die alte.
Beim Release `1.0.6` lagen zwischen dem ersten und dem letzten Package rund 25
Minuten.
Du triffst dieses Fenster leicht, denn ein Update betrifft immer mehrere
Packages: alle `@mittwald/flow-*`-Packages teilen sich eine gemeinsame Version,
und `@mittwald/flow-react-components` fordert `@mittwald/flow-icons-pro` als
Peer-Dependency in exakt derselben Version. Ziehst du in diesem Fenster alle
Flow-Abhängigkeiten auf die neue Version, schlägt die Installation für das
Package fehl, das noch nicht dran war:
```
npm error code ETARGET
npm error notarget No matching version found for @mittwald/flow-react-components@1.0.6
```
pnpm meldet dasselbe als `ERR_PNPM_NO_MATCHING_VERSION`.
Das ist keine Lücke im Release: sobald der Release-Lauf durch ist, haben alle
Packages dieselbe Version. Direkt nach einem Release ist dieser Fehler fast
immer das Veröffentlichungsfenster und nichts anderes.
Warte ein paar Minuten und installiere erneut
Das Fenster schließt sich von selbst. Du brauchst keinen Workaround – pinne
nichts fest und mische keine Flow-Versionen, um den Fehler zu umgehen.
---
# Releases
Diese Seite bündelt die veröffentlichten Flow-Releases in einer Zeitleiste – mit
Highlights, Migrationshinweisen und den enthaltenen Fixes je Patch-Version. Wie
du ein Update einspielst, beschreibt [Upgrades](https://flow.mittwald.de/get-started/upgrades).
---
# Design Tokens
Design Tokens übersetzen grundlegende Designentscheidungen wie Farben, Abstände
und mehr in eine einheitliche Sprache. Entwickler und Designer nutzen diese
Sprache, um ein konsistentes, skalierbares und wiederverwendbares Design in der
gesamten Benutzeroberfläche sicherzustellen.
---
# Was sind Design Tokens?
Ein Design Token (kurz: Token) besteht aus 2 Teilen: Name und Wert. Der Name
eines Tokens folgt einem klaren und verständlichen Benennungsschema. Der Wert
kann alles beinhalten, was zum Erstellen eines Designs notwendig ist: Eine Farbe
in RGB, eine Schriftart, eine Animations-Geschwindigkeit oder einen anderen
Token.
---
# Vorteile
- **Verringerter Wartungsaufwand:** Tokens können aufeinander basieren und die
Eigenschaften eines anderen Tokens erben. Dadurch wirken sich Änderungen an
einem Token-Wert automatisch auf alle Stellen aus, an denen er verwendet oder
referenziert wird. Das spart Zeit und reduziert Fehler.
- **Verbesserte Zusammenarbeit:** Design und Development nutzen mit Tokens eine
gemeinsame Sprache. Dadurch lassen sich Designentscheidungen leicht
verständlich ausdrücken und ebenso einfach im Code implementieren.
- **Einheitliches Design:** Farben, Schriften, Größen und mehr werden überall
einheitlich verwendet.
- **Wiederverwendbarkeit:** Tokens lassen sich problemlos in mehreren Projekten
und Frontends gleichzeitig verwenden.
---
# Benennung von Tokens
Unsere Tokens sind leicht verständlich und nachvollziehbar benannt, um komplexe
Informationen konsistent und selbsterklärend zu vermitteln.
## Schema
Jeder Token-Name folgt einem dreistufigen Benennungsschema: Context, Common Unit
und Clarification. Dieses Schema beginnt mit einer allgemeinen Einordnung und
wird von Stufe zu Stufe immer detaillierter.
**ℹ️ Token-Benennungsschema**
```tsx
import {
Badge,
ColumnLayout,
Text,
} from "@mittwald/flow-react-components";
Context
Einordnung
z. B. Farbe, Component
Common Unit
Kategorie
z. B. Variante, Sizing, Styling
Clarification
Spezifizierung
z. B. Farbwert, State
```
### Context
Der Context stellt den allgemeinsten Zusammenhang dar, auf den ein Token sich
beziehen kann.
### Common Unit
Die Common Unit ordnet den Token in eine spezifische Kategorie innerhalb des
Context ein.
### Clarification
Die Clarification ist die präziseste Ebene, die den Token klar definiert und
abgrenzt.
## Beispiele
**color--hosting-blue--800**
Der Token `color--hosting-blue--800` definiert eine spezifische
[Color](https://flow.mittwald.de/foundations/design/colors). `color` gibt den **Context** an. Der Name
der Farbe `hosting-blue` ist die **Common Unit**. Die Zahl `800` ist die
**Clarification**, die die Abstufung der Farbe definiert und auf den genauen
Farbwert `#0054F5` im Designsystem verweist.
**primary-solid-background-color--default**
Dieser Token definiert die primäre Hintergrundfarbe einer „Solid“-Variante.
`primary` gibt als Context an, dass es sich um eine primäre Farbe handelt. Die
Common Unit `solid-background-color` verdeutlicht, dass die Farbe als
Hintergrundfarbe in der „Solid“-Variante verwendet wird. Die **Clarification**
`default` gibt an, dass der Token für den Default-State verwendet wird.
**alert--info-border-color**
Dieser Token definiert eine Designentscheidung für eine spezifische Component,
in diesem Fall einen [Alert](https://flow.mittwald.de/components/status/alert). `alert` liefert den
**Context** und stellt klar, dass der Token nur für diese Component verwendet
wird. `info-border-color` als **Common Unit** kategorisiert den Token weiter
ein: Er wird für die Border-Color eines Alerts in der „Info“-Variante verwendet.
Eine **Clarification** braucht dieser Token nicht, weil die Border-Color eines
Alerts in allen States gleich bleibt.
---
# Token-Arten und Struktur
Im Benennungsschema haben wir bereits Tokens mit unterschiedlicher Komplexität
betrachtet. In diesem Abschnitt erklären wir die Konzepte, die den verschiedenen
Token-Arten und deren Struktur zugrunde liegen.
## Global Token
Diese Tokens bilden die Grundlage des gesamten Designsystems. Sie definieren
grundlegende Eigenschaften wie Farben, Schriftarten und Abstände, die überall im
System verwendet werden können. Global Tokens sollten nur eingesetzt werden,
wenn der Verwendung kein tieferes Konzept zugrunde liegt. Ein gutes Beispiel
dafür sind Farben, die einen rein dekorativen Zweck erfüllen. Diese müssen nicht
weiter definiert oder in eine Kategorie eingeordnet werden, da sie einen rein
optischen Nutzen erfüllen und keiner tieferen Idee folgen.
| Token-Name | Wert |
| ------------------------ | ------- |
| color--hosting-blue--800 | #0054F5 |
| size-px--s | 8px |
## Alias Token
Alias Tokens referenzieren andere Tokens und weisen ihnen eine spezifischere
Bedeutung zu. Sie helfen dabei, allgemeine Werte in spezifischen Kontexten
einzusetzen. Ein Alias Token konkretisiert z. B. bei einer Color, in welchem
Zusammenhang (Primary, Success, Danger, ...) sie genutzt werden soll. Sie
beziehen sich immer auf einen Global Token und dürfen keine hard coded Werte
besitzen.
Wir nutzen diese Ebene vor allem für die Definition von Tokens, die sich keiner
bestimmten Variante (Solid, Soft, Outlined, Plain) zuordnen lassen, aber in eine
gemeinsame Kategorie zusammengefasst werden können.
| Token-Name | Wert |
| ------------------- | ------------------------ |
| primary--color--800 | color--hosting-blue--800 |
| border-width--400 | size-px--s |
## Alias Token 2
Tokens dieser Ebene besitzen einen noch höheren Detailgrad. Sie werden oft einer
spezifischen Variante zugeordnet und fügen weitere Details hinzu, die über den
ersten Alias Token hinausgehen. Dabei achten wir auch darauf, die Token so zu
definieren, dass sie innerhalb der Variante gut harmonieren. So legen wir z. B.
bei einer bestimmten Variante die Farben so fest, dass die Content-Farbe optisch
zur Hintergrundfarbe passt und barrierefrei lesbar ist.
Ein Alias Token 2 darf keine hard coded Werte besitzen, sich aber sowohl auf
Alias als auch Global Token beziehen.
| Token-Name | Wert |
| --------------------------------------- | ------------------- |
| primary-solid-background-color--default | primary--color--800 |
| info-outline-border-color | info--color--800 |
## Component Specific Token
Diese Tokens sind speziell auf einzelne Components zugeschnitten. Sie vermitteln
einzigartige Designentscheidungen, die ausschließlich für eine bestimmte
Component relevant sind. Sie enthalten den höchsten Detailgrad der
Token-Struktur und sind somit das Gegenteil eines Global Tokens.
| Token-Name | Wert |
| ----------------------------------------------- | --------------------------------------- |
| button--primary-solid-background-color--default | primary-solid-background-color--default |
| alert--info-border-color | info-outline-border-color |
---
# Color
Ausgewählte Colors verkörpern die Marke und dienen gleichzeitig als wesentliches
Kommunikationsmittel in der Benutzeroberfläche. Sie lenken die Aufmerksamkeit
des Users und vermitteln gezielt wichtige Informationen wie z. B. den Status.
---
# Base Color
Base Colors bilden das Fundament des Flow Design Systems. Sie prägen die
Oberflächen und schaffen die visuelle Grundlage für alle Components und Layouts.
## Neutral Color
Neutral Colors sind zurückhaltend und vielseitig einsetzbar. Durch ihre
Ausgewogenheit harmonieren sie mit allen weiteren Farben und kommen in nahezu
allen UI-Elementen zum Einsatz.
| Token | Wert |
| --- | --- |
| `--color--gray--100` | `#FFFFFF` |
| `--color--gray--200` | `#F4F8FC` |
| `--color--gray--300` | `#E5EFF8` |
| `--color--gray--400` | `#DCE9F7` |
| `--color--gray--500` | `#B7C9DB` |
| `--color--gray--600` | `#8FA1B4` |
| `--color--gray--700` | `#5E6C7A` |
| `--color--gray--800` | `#3A434E` |
| `--color--gray--900` | `#292F37` |
| `--color--gray--1000` | `#1B1F24` |
| `--color--gray--1100` | `#121518` |
| `--color--gray--1200` | `#000000` |
## Hosting Blue
Hosting Blue ist unsere Primärfarbe. Sie verleiht dem mStudio ein prägnantes
Erscheinungsbild und stärkt die Wiedererkennung der Marke mittwald. Aufgrund
ihrer Leuchtkraft eignet sie sich besonders für gezielte Akzente, interaktive
Elemente und visuelle Hervorhebungen.
| Token | Wert |
| --- | --- |
| `--color--hosting-blue--100` | `#F0F5FF` |
| `--color--hosting-blue--200` | `#E0EBFF` |
| `--color--hosting-blue--300` | `#ABC6FF` |
| `--color--hosting-blue--400` | `#85AFFF` |
| `--color--hosting-blue--500` | `#6197FF` |
| `--color--hosting-blue--600` | `#3D80FF` |
| `--color--hosting-blue--700` | `#1A68FF` |
| `--color--hosting-blue--800` | `#0054F5` |
| `--color--hosting-blue--900` | `#003FB8` |
| `--color--hosting-blue--1000` | `#002A7B` |
| `--color--hosting-blue--1100` | `#00205C` |
---
# Functional Color
Functional Colors kommunizieren Zustände und unterstützen die Nutzerführung. Sie
helfen dabei, Informationen schnell einzuordnen und Handlungsbedarf zu erkennen.
Weitere Details zur Anwendung finden sich im
[Informationskonzept](https://flow.mittwald.de/foundations/content-guidelines/informationskonzept).
## Espelkamp Green
Espelkamp Green steht für positive Rückmeldungen und erfolgreiche Aktionen. Die
Farbe signalisiert Bestätigung, Abschluss oder einen fehlerfreien Zustand.
| Token | Wert |
| --- | --- |
| `--color--espelkamp-green--100` | `#DCFFF4` |
| `--color--espelkamp-green--200` | `#C0FFEB` |
| `--color--espelkamp-green--300` | `#87FFDA` |
| `--color--espelkamp-green--400` | `#4FFFC9` |
| `--color--espelkamp-green--500` | `#17FFB8` |
| `--color--espelkamp-green--600` | `#00DE9A` |
| `--color--espelkamp-green--700` | `#00A673` |
| `--color--espelkamp-green--800` | `#00825B` |
| `--color--espelkamp-green--900` | `#005F42` |
| `--color--espelkamp-green--1000` | `#003B29` |
| `--color--espelkamp-green--1100` | `#00291C` |
## Royal Orange
Royal Orange kennzeichnet Warnungen und weist auf potenzielle Risiken oder
unerwünschte Zustände hin. Die Farbe signalisiert, dass Aufmerksamkeit
erforderlich ist und eine Handlung zeitnah erfolgen sollte, auch wenn noch kein
kritischer Fehler vorliegt.
| Token | Wert |
| --- | --- |
| `--color--royal-orange--100` | `#FFF4EB` |
| `--color--royal-orange--200` | `#FFEBDC` |
| `--color--royal-orange--300` | `#FFD9BD` |
| `--color--royal-orange--400` | `#FFC89F` |
| `--color--royal-orange--500` | `#FFA562` |
| `--color--royal-orange--600` | `#FF9343` |
| `--color--royal-orange--700` | `#FF8225` |
| `--color--royal-orange--800` | `#F56800` |
| `--color--royal-orange--900` | `#C25200` |
| `--color--royal-orange--1000` | `#8F3D00` |
| `--color--royal-orange--1100` | `#763200` |
## Amaranth Red
Amaranth Red kennzeichnet kritische Zustände. Die Farbe signalisiert dringenden
Handlungsbedarf oder Fehler, die unmittelbare Aufmerksamkeit erfordern.
| Token | Wert |
| --- | --- |
| `--color--amaranth-red--100` | `#FDF0F3` |
| `--color--amaranth-red--200` | `#FBE0E6` |
| `--color--amaranth-red--300` | `#F7C1CC` |
| `--color--amaranth-red--400` | `#F2A2B3` |
| `--color--amaranth-red--500` | `#EE8299` |
| `--color--amaranth-red--600` | `#EA637F` |
| `--color--amaranth-red--700` | `#E54366` |
| `--color--amaranth-red--800` | `#E1244C` |
| `--color--amaranth-red--900` | `#AB1737` |
| `--color--amaranth-red--1000` | `#710F24` |
| `--color--amaranth-red--1100` | `#540B1B` |
---
# Decorative Color
Decorative Colors erweitern die Farbpalette um zusätzliche Markenfarben. Sie
werden ausschließlich für dekorative Elemente sowie für Marketing- und
Kommunikationsmaßnahmen eingesetzt und haben keine funktionale Bedeutung
innerhalb des Interfaces.
## Soft Contrast Violet
| Token | Wert |
| --- | --- |
| `--color--soft-contrast-violet--100` | `#F4F3FE` |
| `--color--soft-contrast-violet--200` | `#EAE7FD` |
| `--color--soft-contrast-violet--300` | `#D6D0FA` |
| `--color--soft-contrast-violet--400` | `#C2B9F8` |
| `--color--soft-contrast-violet--500` | `#AEA2F5` |
| `--color--soft-contrast-violet--600` | `#998BF3` |
| `--color--soft-contrast-violet--700` | `#8574F0` |
| `--color--soft-contrast-violet--800` | `#715DEE` |
| `--color--soft-contrast-violet--900` | `#5d4abf` |
| `--color--soft-contrast-violet--1000` | `#483b9f` |
| `--color--soft-contrast-violet--1100` | `#302a6f` |
## Super Teal
| Token | Wert |
| --- | --- |
| `--color--super-teal--100` | `#ebfdff` |
| `--color--super-teal--200` | `#cffaff` |
| `--color--super-teal--300` | `#96f3ff` |
| `--color--super-teal--400` | `#5eedff` |
| `--color--super-teal--500` | `#26e7ff` |
| `--color--super-teal--600` | `#00d3ed` |
| `--color--super-teal--700` | `#00a1b5` |
| `--color--super-teal--800` | `#008696` |
| `--color--super-teal--900` | `#006b78` |
| `--color--super-teal--1000` | `#004f59` |
| `--color--super-teal--1100` | `#00424a` |
## Bright Lilac
| Token | Wert |
| --- | --- |
| `--color--bright-lilac--100` | `#f9e9ff` |
| `--color--bright-lilac--200` | `#f6dfff` |
| `--color--bright-lilac--300` | `#f1caff` |
| `--color--bright-lilac--400` | `#ebb6ff` |
| `--color--bright-lilac--500` | `#e6a1ff` |
| `--color--bright-lilac--600` | `#e08dff` |
| `--color--bright-lilac--700` | `#ce4bff` |
| `--color--bright-lilac--800` | `#bc08ff` |
| `--color--bright-lilac--900` | `#9000c5` |
| `--color--bright-lilac--1000` | `#5f0083` |
| `--color--bright-lilac--1100` | `#470062` |
## mittwald Navy
| Token | Wert |
| --- | --- |
| `--color--mittwald-navy--100` | `#E4E7F6` |
| `--color--mittwald-navy--200` | `#C8CFEE` |
| `--color--mittwald-navy--300` | `#A5B0E2` |
| `--color--mittwald-navy--400` | `#8291D7` |
| `--color--mittwald-navy--500` | `#5F73CC` |
| `--color--mittwald-navy--600` | `#3D55C1` |
| `--color--mittwald-navy--700` | `#32459E` |
| `--color--mittwald-navy--800` | `#27367B` |
| `--color--mittwald-navy--900` | `#1D285C` |
| `--color--mittwald-navy--1000` | `#131B3D` |
| `--color--mittwald-navy--1100` | `#0E142E` |
---
# Light und Dark Color
Light und Dark Colors wurden speziell für den Einsatz auf farbigen Hintergründen
oder Bildern entwickelt. Sie bestehen aus jeweils 8 transparenten Abstufungen.
Da die Farben auf Transparenzen basieren, muss der Kontrast zur jeweiligen
Hintergrundfarbe eigenständig auf Barrierefreiheit geprüft werden. Aus diesem
Grund stehen sie nur in definierten Components zur Verfügung, beispielsweise im
[Button](https://flow.mittwald.de/components/actions/button).
Die Verwendung orientiert sich am HSL-Farbmodell. HSL steht für Hue, Saturation
und Lightness. Für die Auswahl von Light oder Dark ist ausschließlich der
Lightness-Wert relevant. Er beschreibt die wahrgenommene Helligkeit einer Farbe
auf einer Skala von 0 bis 100.
Als Schwellenwert gilt 50. Liegt der Lightness-Wert **unter 50**, sollte
**Light** verwendet. Liegt der Lightness-Wert **über 50**, sollte **Dark**
verwendet.
Grundsätzlich gilt: Je weiter der Lightness-Wert vom Schwellenwert 50 entfernt
ist, desto größer ist in der Regel der erreichbare Kontrast. Werte deutlich
unter 50 verbessern den Kontrast mit Light, Werte deutlich über 50 verbessern
den Kontrast mit Dark.
Themes berücksichtigen
Bei der Verwendung von Light und Dark auf individuellen Hintergründen sollte
das Verhalten beim Theme-Wechsel bewusst gesteuert werden. Nutze dafür die
Colors Light Static und Dark Static.
Mehr zu Themes
## Light
Verwende die Light-Color, bei dunklen Hintergründen mit einem HSL Lightness-Wert
von weniger als 50.
| Token | Wert |
| --- | --- |
| `--color--white-alpha--100` | `rgb(255 255 255 / 12.5%)` |
| `--color--white-alpha--200` | `rgb(255 255 255 / 25%)` |
| `--color--white-alpha--300` | `rgb(255 255 255 / 37.5%)` |
| `--color--white-alpha--400` | `rgb(255 255 255 / 50%)` |
| `--color--white-alpha--500` | `rgb(255 255 255 / 62.5%)` |
| `--color--white-alpha--600` | `rgb(255 255 255 / 75%)` |
| `--color--white-alpha--700` | `rgb(255 255 255 / 87.5%)` |
| `--color--white-alpha--800` | `rgb(255 255 255 / 100%)` |
## Dark
Verwende die Dark-Color, bei hellen, farbigen Hintergründen mit einem HSL
Lightness-Wert von größer als 50.
| Token | Wert |
| --- | --- |
| `--color--black-alpha--100` | `rgb(0 0 0 / 12.5%)` |
| `--color--black-alpha--200` | `rgb(0 0 0 / 25%)` |
| `--color--black-alpha--300` | `rgb(0 0 0 / 37.5%)` |
| `--color--black-alpha--400` | `rgb(0 0 0 / 50%)` |
| `--color--black-alpha--500` | `rgb(0 0 0 / 62.5%)` |
| `--color--black-alpha--600` | `rgb(0 0 0 / 75%)` |
| `--color--black-alpha--700` | `rgb(0 0 0 / 87.5%)` |
| `--color--black-alpha--800` | `rgb(0 0 0 / 100%)` |
---
# Categorical Color
Categorical Colors eignen sich hervorragend, um Daten optisch in Kategorien zu
unterscheiden. Die Farben sind so optimiert, dass sie durch hohe Kontraste auch
für Menschen mit einer Farbsehschwäche gut unterscheidbar sind. Am besten
funktionieren sie in der hier gegebenen Reihenfolge.
**Achtung:** Categorical Colors sind ausschließlich für Datenvisualisierung
gedacht und heben sich bewusst von allen anderen Farben im Design System ab.
Darum sollten sie nicht für dekorative Zwecke oder andere UI-Elemente verwendet
werden.
| Token | Wert |
| --- | --- |
| `--color--categorical--sea-green` | `#0fb5ae` |
| `--color--categorical--palatinate-blue` | `#4046ca` |
| `--color--categorical--tangerine` | `#f68511` |
| `--color--categorical--magenta` | `#de3d82` |
| `--color--categorical--tropical-indigo` | `#7e84fa` |
| `--color--categorical--malachite` | `#72e06a` |
| `--color--categorical--azure` | `#147af3` |
| `--color--categorical--violet` | `#7326d3` |
| `--color--categorical--yellow` | `#e8c600` |
| `--color--categorical--alloy-orange` | `#cb5d00` |
| `--color--categorical--green` | `#008f5d` |
| `--color--categorical--lime` | `#bce931` |
---
# Themes
Themes definieren die visuelle Gestaltung einer Anwendung, ohne die zugrunde
liegende Struktur oder Semantik zu verändern.
Aktuell unterstützt Flow zwei Themes:
- Light
- Dark
Beide basieren auf denselben Design Tokens und Components. Ein Theme ist damit
kein separater Modus mit eigener Logik, sondern eine alternative Darstellung
derselben UI.
---
# Umgang mit Themes
Flow stellt die Grundlage für Themes bereit, übernimmt jedoch nicht automatisch
das Theme-Handling in der Anwendung.
Das Design System stellt bereit:
- Design Tokens für Light und Dark
- Components, die diese Tokens verwenden
Die Anwendung ist verantwortlich für:
- Festlegen des aktiven Themes
- Umschalten zwischen Themes
- Berücksichtigung der Browsereinstellung
- Speicherung der Nutzerpräferenz
---
# Individuelle Inhalte
Bei der Umsetzung sollte besonders auf Stellen geachtet werden, an denen nicht
ausschließlich Tokens verwendet werden, zum Beispiel:
- Custom Farben und Farbverläufe
- Bilder, Illustrationen oder Media Assets
Diese Inhalte reagieren nicht automatisch auf Theme-Wechsel und müssen bewusst
so gestaltet werden, dass sie in beiden Themes funktionieren.
In bestimmten Fällen ist es sinnvoll, dass einzelne UI-Elemente nicht auf einen
Theme-Wechsel reagieren. Zum Beispiel, wenn Components auf festen Hintergründen
wie Bildern oder Farbverläufen eingesetzt werden, die sich beim Wechsel des
Themes nicht verändern. Für solche Szenarien stellt Flow die Farben Light Static
und Dark Static bereit. Diese bleiben unabhängig vom aktiven Theme unverändert
und sorgen für konsistente Lesbarkeit.
Für die Verwendung von Light Static und Dark Static gelten die gleichen
Richtlinien wie für
[Light und Dark Colors](https://flow.mittwald.de/foundations/design/colors#light-und-dark-color).
**ℹ️ Light Theme**
```tsx
import {
AccentBox,
Heading,
LayoutCard,
Section,
Text,
} from "@mittwald/flow-react-components";
<>
Nicht StaticDie Farben passen sich an das Theme an.Static
Die Farben bleiben in Dark und Light Theme gleich.
>
```
**ℹ️ Dark Theme**
```tsx
import {
AccentBox,
Heading,
LayoutCard,
Section,
Text,
} from "@mittwald/flow-react-components";
<>
Nicht StaticDie Farben passen sich an das Theme an.Static
Die Farben bleiben in Dark und Light Theme gleich.
>
```
---
# Technische Implementierung
Das Theme wird über das Attribut `data-theme` auf dem `:root`-Element gesteuert.
Verfügbare Werte sind:
- `light`
- `dark`
- `system`
Wird kein Theme gesetzt oder explizit `data-theme="system"` verwendet, dient die
bevorzugte Browsereinstellung des Nutzers als Grundlage für die Darstellung.
## @teispace/next-themes
Für die Verwaltung und das Umschalten des Themes kann @teispace/next-themes
verwendet werden. Die Bibliothek übernimmt dabei das Speichern des gewählten
Themes und das Setzen des `data-theme` Attributs.
### ThemeProvider
Der `ThemeProvider` wird in der Root der Anwendung eingebunden und steuert, wie
das Theme im DOM abgebildet wird:
```tsx
import { ThemeProvider } from "@teispace/next-themes";
{children};
```
### Theme wechseln mit `useTheme`
Innerhalb von Client Components kann das Theme über den Hook `useTheme`
gesteuert werden:
```ts
import { useTheme } from "@teispace/next-themes";
const { theme, setTheme } = useTheme();
```
#### Verfügbare Werte:
- `theme` → aktuelles gesetztes Theme (`light`, `dark`, `system`)
- `setTheme("light" | "dark" | "system")` → wechselt das Theme
---
# States
States beschreiben den Zustand einer Komponente und ihre Reaktion auf
Interaktionen. Flow stellt die States für alle Komponenten bereit.
---
# Interaktions-States
Interaktions-States entstehen, wenn Nutzer mit einer Komponente interagieren.
Die Komponenten zeigen sie automatisch an.
## Default
Default ist der Ruhezustand einer Komponente, in dem keine Interaktion
stattfindet. Alle anderen States bauen visuell auf ihm auf.
```tsx
import { Button } from "@mittwald/flow-react-components";
```
## Hover
Hover zeigt an, dass sich der Mauszeiger über einem interaktiven Element
befindet. Der State signalisiert, dass das Element bedienbar ist. Auf
Touch-Geräten gibt es keinen Hover, wichtige Informationen dürfen deshalb nie
nur im Hover sichtbar sein.
```tsx
import { Button } from "@mittwald/flow-react-components";
```
## Pressed
Pressed zeigt an, dass ein Element gerade gedrückt wird. Das direkte Feedback
bestätigt dem Nutzer, dass seine Eingabe angekommen ist.
```tsx
import { Button } from "@mittwald/flow-react-components";
```
## Focus
Focus zeigt an, welches Element gerade per Tastatur angesteuert ist. Der
Focus-Ring erscheint nur bei der Bedienung mit der Tastatur und ist für Nutzer,
die ohne Maus navigieren, die wichtigste Orientierung. Der Focus darf deshalb
nie verloren gehen. Mehr dazu findest du unter
[Accessibility](https://flow.mittwald.de/foundations/design/accessibility).
```tsx
import { Button } from "@mittwald/flow-react-components";
```
## Selected
Selected zeigt an, dass eine Option ausgewählt oder aktiv ist. Typische
Komponenten sind [Checkbox](https://flow.mittwald.de/components/form-controls/checkbox),
[RadioGroup](https://flow.mittwald.de/components/form-controls/radio-group) und
[Tabs](https://flow.mittwald.de/components/structure/tabs).
```tsx
import { Checkbox } from "@mittwald/flow-react-components";
Newsletter abonnieren
```
---
# Component-States
Diese States werden über Properties gesetzt. Sie beschreiben, ob und wie eine
Komponente bedient werden kann.
## Disabled
Disabled zeigt an, dass ein Element aktuell nicht bedient werden kann. Der Wert
eines deaktivierten Felds wird beim Absenden eines Formulars nicht übermittelt.
Mache aus dem Kontext klar, warum das Element nicht zur Verfügung steht, damit
Nutzer wissen, was sie tun müssen. Neben dem
[Button](https://flow.mittwald.de/components/actions/button) lassen sich auch Eingabefelder wie
[TextField](https://flow.mittwald.de/components/form-controls/text-field) oder
[Checkbox](https://flow.mittwald.de/components/form-controls/checkbox) deaktivieren.
```tsx
import { Button } from "@mittwald/flow-react-components";
```
## Read-only
Nur mit erkennbarem Kontext verwenden
Ein Read-only-Feld unterscheidet sich optisch nicht von einem bedienbaren
Feld. Aus dem Kontext muss deshalb klar hervorgehen, dass nichts bearbeitet
werden kann, zum Beispiel durch eine als Vorschau gekennzeichnete Ansicht.
Read-only zeigt einen Wert an, der relevant ist, aber nicht geändert werden
kann. Anders als bei Disabled bleibt der Wert gut lesbar, lässt sich markieren
und kopieren und wird mit dem Formular übermittelt. Typische Komponenten sind
[TextField](https://flow.mittwald.de/components/form-controls/text-field),
[Select](https://flow.mittwald.de/components/form-controls/select) und
[Rating](https://flow.mittwald.de/components/form-controls/rating).
```tsx
import {
Label,
TextField,
} from "@mittwald/flow-react-components";
```
## Invalid
Invalid zeigt an, dass eine Eingabe fehlerhaft ist. Kombiniere den State immer
mit einem `FieldError`, der erklärt, wie Nutzer den Fehler beheben. Wie du gute
Fehlermeldungen formulierst, erfährst du unter
[Fehlermeldungen](https://flow.mittwald.de/foundations/content-guidelines/fehlermeldungen).
```tsx
import {
FieldError,
Label,
TextField,
} from "@mittwald/flow-react-components";
Gib eine gültige Domain ein.
```
## Required
Required kennzeichnet ein Pflichtfeld, das ausgefüllt werden muss, bevor ein
[Form](https://flow.mittwald.de/components/react-hook-form/form) abgeschickt werden kann. Flow
kennzeichnet dabei nicht die Pflichtfelder, sondern die optionalen: Ihr Label
erhält automatisch den Zusatz „(optional)“.
```tsx
import {
Label,
TextField,
} from "@mittwald/flow-react-components";
```
## Indeterminate
Indeterminate zeigt an, dass nur ein Teil der untergeordneten Optionen
ausgewählt ist. Der State kommt vor allem bei einer
[Checkbox](https://flow.mittwald.de/components/form-controls/checkbox) zum Einsatz, die eine
[CheckboxGroup](https://flow.mittwald.de/components/form-controls/checkbox-group) oder die Auswahl in
einer Liste zusammenfasst.
```tsx
import { Checkbox } from "@mittwald/flow-react-components";
Alle Projekte auswählen
```
## Pending, Succeeded und Failed
Diese States geben dem Nutzer Feedback zu einer Aktion, die im Hintergrund
läuft: Pending zeigt an, dass die Aktion läuft, Succeeded, dass sie erfolgreich
abgeschlossen wurde, und Failed, dass sie fehlgeschlagen ist. Steuere die States
über die [Action](https://flow.mittwald.de/components/actions/action)-Komponente. Sie setzt den
passenden State automatisch anhand des Verlaufs der Aktion.
```tsx
import { Button } from "@mittwald/flow-react-components";
<>
>
```
---
# Accessibility
Accessibility bedeutet, digitale Produkte so zu gestalten, dass möglichst viele
Menschen sie nutzen können. Dazu gehören Menschen mit dauerhaften
Einschränkungen ebenso wie Personen mit temporären oder situativen
Herausforderungen, etwa bei schlechter Beleuchtung oder mit nur einer freien
Hand. Gute Accessibility verbessert die Nutzung für **alle**.
Unser Design System Flow bildet dafür eine wichtige Grundlage. Die Components
bringen bereits zugängliche Interaktionen, eine sinnvolle Struktur und
unterstützende Attribute mit. Echte Accessibility entsteht jedoch erst im
Zusammenspiel mit der Umsetzung im Produkt. Flow unterstützt dich dabei, ersetzt
aber keine bewussten Entscheidungen in deinem Projekt.
Die Anforderungen der
[Web Content Accessibility Guidelines (WCAG) 2.2 AA](https://www.w3.org/TR/WCAG22/)
dienen Flow als Referenz für eine zugängliche Gestaltung. Sie fließen in
Components, Patterns und Grundlagen – wie beispielsweise Kontraste – ein und
werden kontinuierlich berücksichtigt und weiterentwickelt.
---
# Was Flow für dich übernimmt
Unsere Components sind so gestaltet, dass sie automatisch viele typische
Anforderungen an Accessibility erfüllen.
Dazu gehören zum Beispiel:
- ausreichende Kontraste
- Tastaturbedienbarkeit
- unterstützende ARIA-Attribute an geeigneten Stellen
- einheitliche Interaktionsmuster und Patterns
Wenn du die Components wie vorgesehen verwendest, hast du bereits eine solide
Grundlage für Accessibility.
---
# Was du selbst beachten solltest
Accessibility entsteht erst durch die richtige Verwendung der Components im
Produkt.
Beachte dabei einige Grundlagen:
- **Nutze semantisches HTML:** Verwende strukturelle Elemente wie `header`,
`main` oder `footer`, um Bereiche einer Seite eindeutig zu kennzeichnen, statt
generischer Container ohne Bedeutung.
- **Achte auf eine sinnvolle Struktur der Seite:** Überschriften sollten logisch
aufgebaut sein und Inhalte klar gliedern.
- **Setze ARIA-Attribute bewusst ein:** ARIA ergänzt Semantik, ersetzt sie aber
nicht.
- **Formuliere hilfreiche Alternativtexte für Bilder:** Alt-Texte sollten immer
den Zweck eines Bildes beschreiben. Dekorative Bilder benötigen keinen
Alternativtext.
- **Beschrifte interaktive Elemente verständlich:**
[Links](https://flow.mittwald.de/components/navigation/link) und [Buttons](https://flow.mittwald.de/components/actions/button)
sollten klar sagen, was passiert.
- **Prüfe Farbkontraste bei individuellen Anpassungen.**
---
# Mehr erfahren
Accessibility ist kein Zustand, den man einmal erreicht und dann abhaken kann.
Sie sollte während der Entwicklung regelmäßig geprüft und Schritt für Schritt
verbessert werden.
Flow und die zugehörige Dokumentation bieten dafür eine solide Grundlage und
helfen dir, viele Anforderungen frühzeitig zu berücksichtigen. Es lohnt sich
dennoch, sich zusätzlich mit den offiziellen Richtlinien vertraut zu machen.
Besonders hilfreich sind die
[WCAG 2.2 Guidelines](https://www.w3.org/TR/WCAG22/) sowie die
[ARIA Authoring Practices Guide (APG)](https://www.w3.org/WAI/ARIA/apg/). Dort
findest du konkrete Best Practices, Beispiele für typische Interaktionsmuster
und weiterführende Empfehlungen für barrierearme Interfaces.
---
# Typography
Typography strukturiert den Inhaltsbereich, indem sie eine visuelle Hierarchie
schafft. Darüber hinaus erhöht eine gute Typography die Lesbarkeit und stärkt
die Marke im Erscheinungsbild.
---
Flow nutzt die Schriftfamilie **Inter**, die für Bildschirmdarstellung optimiert
ist und auch in kleinen Größen gut lesbar bleibt. Die einheitliche Verwendung
sorgt für ein ruhiges, konsistentes Erscheinungsbild.
Für Fließtexte wird **Regular**, für Überschriften und Hervorhebungen **Bold**
eingesetzt. Eine Zeilenhöhe (line height) von 150 Prozent zur Schriftgröße (font
size) verbessert die Lesbarkeit und unterstützt die Barrierefreiheit.
Abweichungen sind nur in Ausnahmefällen vorgesehen.
Alle Schriftgrößen werden in **REM** definiert. So bleiben Texte skalierbar,
reagieren auf Zoom und berücksichtigen individuelle Systemeinstellungen.
---
# Schriftgrößen und Hierarchie
Eine klare typografische Hierarchie hilft dabei, Inhalte strukturiert und
schnell erfassbar zu machen.
## Headings
[Headings](https://flow.mittwald.de/components/content/heading) strukturieren Inhalte und definieren
Abschnitte. Jede Stufe hat eine semantische Bedeutung und sollte entsprechend
eingesetzt werden. H1 ist die oberste inhaltliche Ebene einer Seite.
| Preview | REM | Token |
| -------------------------------------- | --------- | ----------------------- |
| Heading 1 | 2.625 rem | `font-size-heading-xxl` |
| Heading 2 | 2 rem | `font-size-heading-xl` |
| Heading 3 | 1.5 rem | `font-size-heading-l` |
| Heading 4 | 1.25 rem | `font-size-heading-m` |
| Heading 5 | 1.125 rem | `font-size-heading-s` |
| Heading 6 | 1 rem | `font-size-heading-xs` |
---
## Body
### Text
[Text](https://flow.mittwald.de/components/content/text) wird für Fließtexte, Beschreibungen und längere
Inhalte verwendet. Small eignet sich für unterstützende oder sekundäre
Informationen. Um Inhalte übersichtlich und schnell erfassbar zu gestalten,
lassen sich zentrale Informationen durch Bold gezielt hervorheben.
| Preview | REM | Token |
| --------------------------------------------- | --------- | ------------------ |
| Text (Body) | 1 rem | `font-size-text-m` |
| Text (Body) | 1 rem | `font-size-text-m` |
| Text (Small) | 0,875 rem | `font-size-text-s` |
| Text (Small) | 0,875 rem | `font-size-text-s` |
### Links
[Links](https://flow.mittwald.de/components/navigation/link) werden je nach Kontext unterschiedlich
gekennzeichnet. Freistehende Verlinkungen sind visuell in Bold hervorgehoben,
während sie im Fließtext durch Unterstreichung kenntlich gemacht werden.
| Preview | REM | Token |
| ----------------------------------- | ----- | ------------------ |
| Link | 1 rem | `font-size-text-m` |
| [Link](https://flow.mittwald.de/components/navigation/link) | 1 rem | `font-size-text-m` |
### Code
Für eine technische Darstellung von Code wird die Schrift **Fira Code**
eingesetzt.
| Preview | REM | Token |
| ------- | --------- | ------------------ |
| `code` | 0,875 rem | `font-size-text-s` |
---
# Layout
Das Layout organisiert die Benutzeroberfläche in einer logischen und
hierarchischen Struktur, um die Aufmerksamkeit des Users gezielt zu lenken.
Dabei werden Elemente wie Header, Navigation und Content-Bereich berücksichtigt.
---
# Best Practices
Ein Layout fügt verschiedene Elemente der Benutzeroberfläche zu einer logischen
Struktur zusammen. Die folgenden Punkte sind hilfreich, um ein gutes Layout zu
erstellen:
- Ein Layout sollte den Fokus immer auf die Aufgaben des Users legen.
- Ein konsistentes Layout hilft dem User, die Website schneller zu verstehen, da
nicht ständig neue Layouts erlernt werden müssen.
- Der Fokus des Layouts sollte auf dem Content-Bereich liegen, da hier die
Hauptaktionen des Users liegen. Achte hier besonders auf die strukturelle
Hierarchie und die inhaltlichen Beziehungen des Contents.
- Ein Layout sollte immer responsiv sein, damit die Benutzeroberfläche auch auf
kleineren Bildschirmen funktioniert. Im Layout ist grade das Zusammenspiel von
Header, Side-Navigation und Content-Bereich für die Responsivität relevant.
---
# Genereller Aufbau
Grob lassen sich die meisten Layouts, die mit Flow erstellt werden können, in 4
Bereiche einteilen: **Header**, **Side-Navigation**, **Content** und
**Overlay**. Das Layout innerhalb dieser Bereiche wird durch verschiedene
Components und deren Komposition bestimmt. In unseren Components ist das
[Spacing](https://flow.mittwald.de/foundations/structure/spacing) bereits integriert.
Verschiedene Layouts mit Code zum Kopieren finden sich unter
[Code Templates](https://flow.mittwald.de/templates/app-shells/simple-app).
**ℹ️ Layout-Aufbau**
Grobes Layout einer Seite. Eingeteilt in Header-, Side-Navigation- und
Content-Bereich.
```tsx
export default () => {
const area: React.CSSProperties = {
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "var(--size-px--xs)",
backgroundColor:
"var(--info-soft-background-color--default)",
border: "1px dashed var(--info-soft-content-color)",
borderRadius: "var(--corner-radius--s)",
};
const vertical: React.CSSProperties = {
writingMode: "vertical-rl",
transform: "rotate(180deg)",
};
const layout: React.CSSProperties = {
alignSelf: "stretch",
height: "280px",
display: "grid",
gridTemplateAreas: `
'header header'
'nav content'
`,
gridTemplateColumns: "1fr 5fr",
gridTemplateRows: "1fr 6fr",
gap: "var(--size-px--s)",
color: "var(--info-soft-content-color)",
fontSize: "var(--font-size-text--s)",
};
return (
);
}
```
## Header
Der Header-Bereich befindet sich im oberen Bereich des Layouts und ist auf jeder
Seite identisch. Er kann das Logo sowie eine
[HeaderNavigation](https://flow.mittwald.de/components/navigation/header-navigation) enthalten. Ein
Hauptbestandteil der HeaderNavigation sind primäre Navigationspunkte. Ergänzend
gibt es [Buttons](https://flow.mittwald.de/components/actions/button), die Overlays oder
[ContextMenus](https://flow.mittwald.de/components/actions/context-menu) öffnen, wie beispielsweise für
die Suche, das NotificationCenter oder das ProfilMenu. Im Header-Bereich sollten
ausschließlich wichtige Elemente angezeigt werden, die auf allen Seiten sichtbar
sein müssen.
## Side-Navigation
Die Side-Navigation befindet sich entweder direkt links neben dem
Content-Bereich oder fest am linken Bildschirmrand. Sie muss nicht nur aus einer
Navigation bestehen, sondern kann auch aus einer Primary und einer Secondary
Navigation bestehen.
Zum Aufbau einer Side-Navigation kann eine
[LayoutCard](https://flow.mittwald.de/components/structure/layout-card) mit der Component
[Navigation](https://flow.mittwald.de/components/navigation/navigation) gefüllt werden.
## Content
Der Content-Bereich stellt dem User Informationen zur Verfügung, damit er seine
Aufgabe erfüllen kann. Der Inhalt variiert von Seite zu Seite, es wird jedoch
empfohlen, den Inhalt nach einem wiederkehrenden Muster anzuordnen, das bereits
auf anderen Seiten verwendet wurde.
Die Navigationsstruktur im Content-Bereich folgt ebenfalls einem klaren Muster.
Im Content-Bereich können [Tabs](https://flow.mittwald.de/components/structure/tabs) und
[List](https://flow.mittwald.de/components/list/list) verwendet werden, um die Inhalte zu teilen oder in
die Tiefe zu navigieren. Bei der Navigation in tiefere Ebenen, z. B. über eine
List, bietet eine [Breadcrumb](https://flow.mittwald.de/components/navigation/breadcrumb) eine
Orientierungshilfe.
Der Content wird von einer [LayoutCard](https://flow.mittwald.de/components/structure/layout-card)
umschlossen. Um innerhalb der LayoutCard die richtigen Abstände in vertikaler
und horizontaler Richtung sicherzustellen, sollten die Layout-Components
[Section](https://flow.mittwald.de/components/structure/section) und
[ColumnLayout](https://flow.mittwald.de/components/structure/column-layout) verwendet werden. Ein
seitenweiter [Alert](https://flow.mittwald.de/components/status/alert#position-seitenweit) steht
ebenfalls in der LayoutCard, vor der ersten Section und nicht oberhalb der
LayoutCard.
## Overlay
Ein Overlay öffnet sich über der eigentlichen Seite. Größere Overlays wie
[Modals](https://flow.mittwald.de/components/overlays/modal) und [OffCanvas](https://flow.mittwald.de/components/overlays/modal)
erhalten zusätzlich einen Backdrop, damit sich der User besser auf den
angezeigten Inhalt konzentrieren kann. Andere, kleinere Overlays wie
[ContextMenu](https://flow.mittwald.de/components/actions/context-menu) oder
[Tooltip](https://flow.mittwald.de/components/overlays/tooltip) erhalten keinen Backdrop für die im
Hintergrund angezeigte Seite.
---
# Spacing
Spacing definiert die vertikalen und horizontalen Abstände zwischen Elementen
und sorgt so für ein harmonisches Layout.
---
# Grundlagen
Die Spacings und generell alle Größen in Flow basieren auf 8-Pixel-Schritten. Da
jedoch oft kleinere Spacings als 8px benötigt werden, stehen zusätzlich feinere
Abstufungen von 4px und 2px zur Verfügung. Standardmäßig folgen alle Components
den definierten Spacings. Für ein optimales Spacing – das Einfluss auf das
Layout hat – sollten die Components
[ColumnLayout](https://flow.mittwald.de/components/structure/column-layout) und
[Section](https://flow.mittwald.de/components/structure/section) verwendet werden.
---
# Best Practices
Um mit dem Spacing ein harmonisches Layout zu erschaffen, achte auf folgende
Punkte:
- Verwende die Layout-Components
[ColumnLayout](https://flow.mittwald.de/components/structure/column-layout) und
[Section](https://flow.mittwald.de/components/structure/section), um dadurch das richtige vertikale
und horizontale Spacing zu erhalten.
- Spacing hilft bei der Gruppierung von Elementen auf Benutzeroberflächen. Je
geringer das Spacing zwischen den Elementen ist, desto größer ist das Gefühl
der Zusammengehörigkeit.
- Optisch kann es sinnvoll sein, das Spacing zwischen größeren Elementen größer
und zwischen kleineren Elementen kleiner zu wählen.
**✅ Do**
Zwischen dem [Avatar](https://flow.mittwald.de/components/content/avatar) und dem Namen und
Untertitel ist ein Spacing von 8px definiert, da diese Größe ein gutes
Zusammengehörigkeitsgefühl vermittelt.
```tsx
import {
Avatar,
Content,
Initials,
Text,
} from "@mittwald/flow-react-components";
Sven Fuchs
Sven Fuchs
Projektentwickler
```
**⛔️ Don't**
Wenn das Spacing zu groß ist, fühlt sich der Avatar nicht mehr zum Namen und
Untertitel gehörig an.
```tsx
import {
Avatar,
Content,
Initials,
Text,
} from "@mittwald/flow-react-components";
Sven Fuchs
Sven Fuchs
Projektentwickler
```
---
# Spacing-Tabelle
Alle Spacings in der Tabelle können sowohl vertikal und horizontal verwendet
werden. Spacings werden in REM angegeben, wenn sie zwischen Elementen eingesetzt
werden, die ebenfalls in REM definiert sind. Dies ist zum Beispiel bei Text der
Fall, damit der Abstand beim Skalieren der Schriftgröße konsistent bleibt.
Zwischen zwei Elementen muss jedoch nicht zwingend ein Spacing verwendet werden,
da nicht immer ein Abstand erforderlich ist.
| Size | Pixel | REM | (Vertical) Visual |
| ----- | ----- | --------- | -------------------------------------------------------------------------------------------------------- |
| `xxs` | 2px | 0,125 rem | |
| `xs` | 4px | 0,25 rem | |
| `s` | 8px | 0,5 rem | |
| `m` | 16px | 1 rem | |
| `l` | 24px | 1,5 rem | |
| `xl` | 32px | 2 rem | |
| `xxl` | 64px | 4 rem | |
---
# Components
Flow unterstützt das Verschachteln von Components, um deine UI aufzubauen. Dies
ist eine kurze Dokumentation darüber, warum wir uns dafür entschieden haben.
---
Die folgenden Codebeispiele werden in der Erklärung referenziert.
```
// Flow
```
vs.
```
// Not Flow
```
---
# Logik
Components-Libraries basieren in der Regel darauf, Components zu verschachteln,
um größere Components zu bauen. Das ist ein weit verbreitetes und effektives
Pattern, das außerdem leicht verständlich ist. Genau deshalb unterstützen wir
dieses Modell auch in Flow. Wenn man zum Beispiel ein
[CounterBadge](https://flow.mittwald.de/components/status/counter-badge) innerhalb eines
[Buttons](https://flow.mittwald.de/components/actions/button) platziert, wird dieses automatisch oben
rechts positioniert – so, wie man es erwarten würde.
---
# Lesbarkeit
Wir haben unsere Frontend-Entwickler gefragt, was am Ende besser lesbar ist:
1. Unterkomponenten über Props zu übergeben, wie das
[Icon](https://flow.mittwald.de/components/content/icon) im Beispiel
2. Das Icon als Child in die Component zu legen.
Wir haben uns für Variante 2 entschieden, da sie besonders bei mehreren
Unterkomponenten besser lesbar ist.
---
# Wiederverwendbarkeit
Entwickler bauen häufig eigene Components, um sie wiederzuverwenden oder um die
Implementierung aufzuteilen. Wenn es zum Beispiel bereits eine
`UnreadNotificationsCounter`-Component gibt, die den Zähler aus der API lädt und
in einem CounterBadge darstellt, kann diese einfach als Child im Button
verwendet werden.
```
```
---
# Suspense- / ErrorBoundaries
Nur mit dem „Components-Ansatz“ ist es möglich, Child-Components in Loading-
oder ErrorBoundaries zu kapseln.
```
```
---
# Aufgeblähtes Props-Interface
Wenn Components anfangen, Features anderer Components nativ zu unterstützen,
wird ihr Props-Interface schnell aufgebläht. Beim Beispiel `counterBadge={7}`
könnte man denken: „Hey, das ist doch nur ein Prop mehr und leicht
verständlich!“ Aber was ist, wenn CounterBadge mehr Eigenschaften als nur count
unterstützt? Jede unterstützte Eigenschaft müsste dann als
`counter[CounterProp]` im Button (und möglicherweise weiteren Parent-Components)
vorhanden sein.
Aufgeblähte Gesamtbibliothek vs. All-Mighty-Components
Wenn Flow für jeden Anwendungsfall spezielle Components anbieten würde, müsste
es z. B. einen `IconButton`, einen `CounterBadgeButton` und streng genommen
sogar einen `IconCounterBadgeButton` geben. Das würde schnell zu sehr vielen
Components führen.
Und was wäre, wenn man stattdessen alles direkt im Button unterstützt? Das führt
meist zu sehr großen „All-Mighty-Components“ ohne klaren Scope und mit
überladener Implementierung.
---
# Children in Unterkomponenten
Nur mit dem „Components-Ansatz“ ist es möglich, Children auch in
Unterkomponenten zu verwenden.
```
```
---
# Baue deine eigenen Components
Wenn du bestimmte Kombinationen von Components häufig in deiner UI verwendest,
kannst du dir einfach eigene Components mit genau den Props bauen, die du
benötigst.
---
# Navigation
Eine Navigation ist ein zentraler Baustein jeder Benutzeroberfläche. Sie hilft
Usern, Inhalte schnell und intuitiv zu finden. Sie verbindet verschiedene
Bereiche miteinander, macht Strukturen verständlich und unterstützt ein
konsistentes Nutzungserlebnis. Je nach Struktur und Komplexität einer Anwendung
kommen unterschiedliche Components zum Einsatz, die häufig sinnvoll miteinander
kombiniert werden.
---
# Best Practices
- **Komplexität reduzieren:** Gestalte Inhalte und Navigationen klar und
verständlich. Eine reduzierte und konsistente Navigation verbessert die
Orientierung und erleichtert die Nutzung.
- **Orientierung schaffen:** User sollten jederzeit erkennen können, wo sie sich
befinden. Aktive Zustände, Breadcrumbs und die TabNavigation unterstützen
dabei.
- **Suche integrieren:** Eine gut sichtbare Suche und Schnellzugriffe
erleichtern besonders in komplexen Anwendungen den schnellen Zugriff auf
Inhalte.
- **Konsistente Navigation:** Navigationsmuster und Positionierungen sollten
innerhalb der gesamten Anwendung konsistent verwendet werden. Wiederkehrende
Muster erleichtern das Verständnis und stärken die Orientierung.
---
# Navigationsstrukturen
Hauptnavigationen ermöglichen den Zugriff auf zentrale Bereiche einer Anwendung.
Ergänzende Navigationskomponenten innerhalb des Contents schaffen Orientierung,
erleichtern den Wechsel zwischen Ansichten und ermöglichen die Navigation auf
tieferer Ebene.
## Hauptnavigation
Es gibt zwei grundlegende Navigationsvarianten: HeaderNavigation und
SideNavigation. Welche eingesetzt wird, hängt vom Layout und der Komplexität der
Anwendung ab. Beachte hierbei die Best Practices der Navigationen.
```tsx
import {
Avatar,
Button,
Flex,
HeaderNavigation,
Heading,
IconSearch,
Initials,
Label,
LayoutCard,
Link,
Navigation,
NavigationGroup,
IconDashboard,
IconMonitoring,
IconApp,
IconContainer,
Text,
ColumnLayout,
} from "@mittwald/flow-react-components";
Dashboard
Projekt
Server
Organisation
Mein Projekt
Dashboard
Monitoring
Apps
Container
Lorem ipsum dolor sit amet consectetur adipisicing
elit. Cumque eius quam quas vel voluptas, ullam
aliquid fugit. Voluptate harum accusantium rerum
ullam modi blanditiis vitae, laborum ea tempore,
dolore voluptas. Earum pariatur, similique corrupti
id officia perferendis. Labore, similique. Earum,
quas in. At dolorem corrupti blanditiis nulla
deserunt laborum! Corrupti delectus aspernatur nihil
nulla obcaecati ipsam porro sequi rem? Quam.
```
### HeaderNaviagtion
Die [HeaderNavigation](https://flow.mittwald.de/components/navigation/header-navigation), befindet sich
im oberen Bereich der Seite und eignet sich besonders für Hauptnavigationen mit
flacher Struktur und übersichtlicher Informationsarchitektur. Zusätzlich zu den
zentralen Navigationspunkten kann sie mit übergeordneten Funktionen ergänzt
werden, wie beispielsweise der Zugang zu einem Profil, eine globale Suche oder
globale Einstellungen.
### Navigation (SideNavigation)
Die [Navigation](https://flow.mittwald.de/components/navigation/navigation) ist vertikal angeordnet und
eignet sich besonders für Seitenlayouts mit viel Platz in der Breite oder für
Anwendungen mit komplexeren Strukturen. Sie bietet ausreichend Platz für viele
Menüpunkte sowie verschachtelte Navigations-Ebenen.
Im mStudio wird die SideNavigation aufgrund der komplexen
Informationsarchitektur sowohl für Hauptnavigationspunkte als auch für
untergeordnete Navigationspunkte genutzt und in einer
[LayoudCard](https://flow.mittwald.de/components/structure/layout-card) dargestellt.
---
## Content-Navigation
Die Components Breadcrumb, TabNavigation und List unterstützen bei der
Orientierung innerhalb des Contents und ermöglichen einen schnellen Wechsel
zwischen zusammengehörenden Bereichen oder das Navigieren in tiefere Ebenen.
```tsx
import {
Flex,
Breadcrumb,
Link,
Heading,
LayoutCard,
TabNavigation,
Section,
Text,
} from "@mittwald/flow-react-components";
Projekt
E-Mails
E-Mail-Adresse
mail@meinprojekt.de
E-Mail-Adressen
Postausgänge
Archivierung
Lorem ipsum dolor sit amet consectetur adipisicing
elit. Cumque eius quam quas vel voluptas, ullam
aliquid fugit. Voluptate harum accusantium rerum
ullam modi blanditiis vitae, laborum ea tempore,
dolore voluptas. Earum pariatur, similique corrupti
id officia perferendis. Labore, similique. Earum,
quas in. At dolorem corrupti blanditiis nulla
deserunt laborum! Corrupti delectus aspernatur nihil
nulla obcaecati ipsam porro sequi rem? Quam.
```
### Breadcrumb
Die [Breadcrumb](https://flow.mittwald.de/components/navigation/breadcrumb) zeigt den aktuellen Pfad
innerhalb einer hierarchischen Struktur an. Sie unterstützt die Orientierung,
indem sie den Usern jederzeit verdeutlicht, wo sie sich befinden. Zusätzlich
erleichtert sie die Rücknavigation zu übergeordneten Ebenen und verbessert damit
die Navigierbarkeit komplexerer Anwendungen.
### TabNavigation
Die [TabNavigation](https://flow.mittwald.de/components/navigation/tab-navigation) ermöglicht das
Navigieren zwischen gleichrangigen Unterseiten. Durch ihre permanente
Sichtbarkeit behalten User den Überblick über die verfügbaren Bereiche und
erkennen am aktiven Zustand, wo sie sich gerade befinden. Reicht die verfügbare
Breite nicht aus, klappen überzählige Punkte automatisch in ein
[ContextMenu](https://flow.mittwald.de/components/actions/context-menu) zusammen.
### Listen, Raster und Tabellen
Die verschiedenen Darstellungen einer [List](https://flow.mittwald.de/components/list/list)
strukturieren Inhalte, schaffen Übersicht über ähnliche Elemente und ermöglichen
die Navigation zu detaillierteren Ansichten.
---
## Ergänzende Navigation
Klassische Navigationen können durch Verlinkungen, eine globale Suche oder
automatische Weiterleitungen ergänzt werden. Gerade in komplexen Anwendungen
möchten User schnell zu bestimmten Inhalten gelangen, ohne sich durch die
gesamte Navigationsstruktur bewegen zu müssen. Ergänzende Navigationskomponenten
ersetzen die Hauptnavigation nicht, sondern erweitern sie gezielt.
### Links
[Links](https://flow.mittwald.de/components/navigation/link) ermöglichen den direkten Wechsel in einen
anderen Bereich der Anwendung oder zu externen Inhalten. User können dabei
selbst entscheiden, ob sie dem Wechsel folgen wollen. Daher sollten Links klar
und aussagekräftig benannt sein, damit erkennbar ist, welches Ziel sie haben.
### Suche
Die [Suche](https://flow.mittwald.de/components/form-controls/search-field) ermöglicht den direkten
Zugriff auf Inhalte, Funktionen oder Bereiche, ohne mehrere Navigationsebenen
durchlaufen zu müssen. Besonders in komplexen Anwendungen mit vielen Inhalten
unterstützt sie User dabei, Informationen effizient und zielgerichtet zu finden.
### Weiterleitungen
Weiterleitungen führen User automatisch oder systemgesteuert zu einer anderen
Ansicht oder einem anderen Bereich der Anwendung. Sie sollten bewusst und
nachvollziehbar eingesetzt werden, da unerwartete Wechsel schnell zu
Orientierungsverlust führen können.
---
# Accessibility
Eine barrierefreie Navigation stellt sicher, dass alle User die Anwendung
zuverlässig bedienen können.
- **Semantische Struktur und ARIA-Rollen:** Verwende semantisch korrekte
HTML-Elemente und ergänze diese bei Bedarf durch passende ARIA-Rollen und
-Attribute. Dadurch können Screenreader Navigationsbereiche korrekt
interpretieren und vermitteln.
- **Fokus-Reihenfolge und Tastaturbedienung:** Navigations-Elemente müssen
vollständig per Tastatur erreichbar und bedienbar sein. Die Fokus-Reihenfolge
sollte logisch aufgebaut sein und der visuellen Struktur folgen.
- **Verständliche Bezeichnungen:** Navigationspunkte sollten eindeutig benannt
sein und verständlich kommunizieren, wohin sie führen. Dadurch wird die
Orientierung zusätzlich verbessert.
---
# Sprach-Guide
Dieser Guide beschreibt, wie wir bei mittwald schreiben. Er gilt für alle Texte
im mStudio. Für Extensions ist er eine Hilfe, damit die Texte zu unserem Produkt
passen. Du findest hier Leitlinien für deine Texte und einen Glossar mit den
Begriffen, die bei mittwald eine eigene Bedeutung haben.
---
# Wie wir sprechen
## Technisch und verständlich
Wir überzeugen mit Inhalten, nicht mit langen und verschachtelten Erklärungen.
Kurze Sätze und einfacher Satzbau halten unsere Texte übersichtlich. Wir drücken
uns technisch, aber verständlich aus, genau mit der technischen Tiefe, die dem
Nutzer weiterhilft.
## Konsistent
Für Aktionen wie Bestellen und Kündigen oder Anlegen und Löschen legen wir feste
Begriffspaare fest. Verwende dafür die Begriffe aus unseren
[Leitlinien](#leitlinien) und dem [Glossar](#glossar). So erleben unsere Nutzer
überall dieselbe Sprache.
---
# Leitlinien
## Aktive Sprache
Wir agieren aktiv. Das zeigen wir auch in unserer Sprache. Passiv ist in
Ordnung, wenn ein System handelt oder der Handelnde keine Rolle spielt, z. B.
bei automatischen Vorgängen: „Nach 7 Tagen wird dein Server gelöscht.“
**✅ Do**
Du hast den Server gelöscht.
**⛔️ Don't**
Der Server wurde von dir gelöscht.
## Sie, Du oder Ihr
Wir sprechen Nutzer mit „Du“ an und im Singular. In Mailings und Tickets
schreiben wir „Du“, „Dein“ und „Deiner“ groß. Im mStudio schreiben wir „du“,
„dein“ und „deiner“ klein, weil die Texte eher Anleitungscharakter haben.
**✅ Do**
Du hast dein Projekt angelegt.
**⛔️ Don't**
Ihr habt euer Projekt angelegt.
## Kurze Sätze
Fass dich kurz. Um Technisches einfach zu formulieren, helfen kurze Sätze.
Faustregel: Satzlänge maximal 1,5 Zeilen. 1 Subjekt pro Satz.
## Klare Gliederung
Dein Text sollte durch Absätze gegliedert sein. Im besten Fall sind diese nicht
länger als 5 Zeilen. Auch Nummerierungen oder Bulletpoints helfen den Lesern,
deinem Text besser zu folgen und Wichtiges schneller wahrzunehmen.
## Wichtige Aspekte hervorheben
Fette zentrale Begriffe, um Wichtiges in den Vordergrund zu stellen. Beschränke
dich dabei auf einzelne Wörter. Ganze Sätze (oder gar Absätze) werden nicht
gefettet.
## KEIN! GRUND! ZU! SCHREIEN!!!
Wir schreiben ohne Versalien und ohne Ausrufezeichen. Ausnahmen sind etablierte
Abkürzungen wie „DSGVO“ und „PHP“ sowie Eigennamen, die selbst ein
Ausrufezeichen enthalten, z. B. „Joomla!“.
## Bindestrich: Wörter koppeln
Zweisprachige Wortkombinationen werden mit einem Bindestrich gekoppelt.
Englische Begriffe werden nicht gekoppelt, deutsche Kombinationen werden
zusammengeschrieben.
**✅ Do**
Ein Tarif-Upgrade kommt selten allein. Erst recht im Cloud Hosting.
**⛔️ Don't**
Ein Tarif Upgrade allein macht noch keinen Sommer. Auch nicht im
Cloud-Hosting.
Akronyme werden mit einem Bindestrich gekoppelt, z. B. „DSGVO-konform“.
Egal ob mittwald Hosting oder mStudio Login: Die Regel ist immer dieselbe.
mittwald und Produktnamen werden niemals mit einem Bindestrich an ein anderes
Wort gekoppelt.
Generell gilt: Im Zweifel für die Lesbarkeit. Macht die korrekte Schreibweise
Probleme (sieht schlecht aus, Umbruch, usw.), missachten wir die Regeln. Das
sollte aber nur die Ausnahme sein.
## mittwald, mStudio und vServer werden IMMER kleingeschrieben
... auch am Satzanfang.
## Gendern
mittwald lebt Gleichberechtigung. Um kurz und prägnant formulieren zu können,
gendern wir jedoch nur dort, wo es Sinn macht, z. B. in Stellenanzeigen.
An Orten mit wenig Platz, etwa im mStudio oder auf Social Media, verwenden wir
die meist verwendete Schreibweise.
## Zahlen
Um die Lesbarkeit zu steigern, schreiben wir alle Zahlen als Ziffern, auch von 0
bis 12.
Im UX-Writing gibt es davon eine Ausnahme: Ist die Zahl für den Nutzer nicht
besonders relevant und gibt keine konkrete Information wieder, kann sie
ausgeschrieben werden.
**✅ Do**
Nur noch ein Schritt bis zum Abschluss.
**⛔️ Don't**
Nur noch 1 Schritt bis zum Abschluss.
Ist die Zahl dagegen eine wichtige Information, bleibt sie eine Ziffer, z. B.
„Nach 7 Tagen wird dein Server gelöscht.“
## Speicherplatzangaben in binären Einheiten
Wir geben Speichergrößen bewusst in binären Einheiten wie KiB, MiB oder GiB an.
Diese folgen dem binären System, z. B. 1 GiB = 1.073.741.824 Bytes = 1024³
Bytes. Dadurch stellen wir technische Genauigkeit sicher und vermeiden
Verwirrung durch uneinheitliche Umrechnungen, wie sie bei dezimalen Angaben
(z. B. KB, MB, GB) häufig auftreten.
## Preise und Umsatzsteuer
Wir geben Preise immer mit dem Hinweis „zzgl. USt.“ an. Die Abkürzung „USt.“
verwenden wir nur dort. Im Text schreiben wir „Umsatzsteuer“, nicht
„Mehrwertsteuer“.
## Abkürzungen
Wir schreiben „inkl.“ in der deutschen Schreibweise und mit Punkt.
## Englischsprachige Inhalte
Deutsch bildet die Grundlage unserer Kommunikation. Für englische Texte
verwenden wir amerikanisches Englisch (US English).
Das betrifft insbesondere Rechtschreibung, Fachbegriffe und UI-Texte. US English
entspricht in der Tech-Welt dem etablierten Standard.
**✅ Do**
color, behavior, organization
**⛔️ Don't**
colour, behaviour, organisation
## Nutzer, nicht Kunde
Im Produkt sprechen wir von „Nutzern“. Den Begriff „Kunde“ verwenden wir nicht.
Wer mit uns einen Vertrag hat, ist ein „Vertragspartner“ (siehe Glossar).
## Technische Fachbegriffe
Etablierte technische Fachbegriffe übersetzen wir nicht, sondern lassen sie
englisch, z. B. „SSH-User“. „Nutzer“ gilt für Personen, die das Produkt nutzen.
Bezeichnet „User“ dagegen ein technisches Konto, bleibt es „User“.
**✅ Do**
Lege einen Cronjob an.
**⛔️ Don't**
Lege eine zeitgesteuerte Aufgabe an.
## AI und KI
Im Kontext von AI Hosting schreiben wir „AI“. Sonst kann „KI“ im Fließtext
verwendet werden.
## Gegenstücke für Aktionen
Wir nennen die Aktion und ihr Gegenstück konsistent: Wer etwas **installiert**,
kann es **deinstallieren**. Wer ein Verb für den Anfang einer Aktion wählt,
nimmt für ihre Umkehrung das passende Gegenstück.
Für die häufigsten Verben legen wir die Paare fest:
| Aktion | Gegenstück | Wann |
| ------------ | -------------- | ----------------------------------------------------------- |
| Anlegen | Löschen | Ein Objekt wird von Grund auf erstellt, die Daten sind weg. |
| Hinzufügen | Entfernen | Ein bestehendes Objekt wird zugeordnet oder wieder gelöst. |
| Bestellen | Kündigen | Eine kostenpflichtige Bestellung wird beendet. |
| Installieren | Deinstallieren | Gilt nur für Software, z. B. Apps und Systemsoftware. |
| Einladen | Ausladen | Gilt für Personen, die Zugriff erhalten. |
Alle anderen Verben folgen demselben Prinzip, ohne dass sie hier stehen.
---
# Glossar
Dieser Glossar erklärt Begriffe, die im Produkt eine eigene Bedeutung haben und
ohne Definition missverstanden werden könnten. Regeln zur Wortwahl stehen in den
Leitlinien.
- **Artikel:** Alle kostenpflichtigen Produkte, z. B. Domains, die hinzubestellt
werden.
- **Organisation:** Der Vertrags- und Abrechnungsrahmen. Hier werden
Zahlungsinformationen und Vertragspartnerdaten hinterlegt.
- **Projekt:** Bündelt alles, was für den Betrieb einer Website oder
Webanwendung nötig ist, z. B. Apps, Domains, Datenbanken und
E-Mail-Postfächer.
- **Ressourcen:** Die Rechenleistung und der Speicherplatz, die ein Server oder
Projekt zur Verfügung hat: vCPU, RAM und Speicherplatz.
- **Server:** Stellt Ressourcen bereit, die sich mehrere Projekte teilen.
- **Umsatzsteuer:** Die Steuer auf unsere Preise. Wir sagen „Umsatzsteuer“,
nicht „Mehrwertsteuer“.
- **Vertragspartner:** Eine natürliche oder juristische Person, die mit uns
einen Vertrag abgeschlossen hat. Das ist nicht zwangsläufig ein Nutzer.
---
# Informationskonzept
Das Informationskonzept beschreibt, wie der User im mStudio über alle relevanten
Informationen informiert wird – sowohl durch selbst initiierte Aktionen als auch
durch systemseitig ausgelöste Ereignisse. Dazu zählen laufende und
abgeschlossene Prozesse sowie wichtige Systemmeldungen (z. B. „Speicherplatz ist
voll“). Es definiert, wie Feedback zu Prozessen gegeben wird und wie
Benachrichtigungen gestaltet sind.
---
# Status
Das Informationskonzept unterscheidet verschiedene Status. Jeder Status folgt
dabei einem definierten Kombination aus [Color](https://flow.mittwald.de/foundations/design/colors)- und
[Icon](https://flow.mittwald.de/components/content/icon#icon-bibliothek), um eine eindeutige und
schnelle Erkennung zu ermöglichen.
Wenn zwei Status inhaltlich miteinander konkurrieren, wird immer der
handlungsentscheidende angezeigt. Zum Beispiel überschreibt ein Danger stets
Warning und Info.
| Status | Einsatz | Beispiel |
| --------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Info | Sachliche Hinweise/Informationen ohne akuten Handlungsbedarf | - Update verfügbar - Download wird vorbereitet - Kündigung vorgemerkt - Neue Nachricht im Ticket - Aktive Anlegeprozesse |
| Warning | Handlungsempfehlung oder Hinweis möglicher Konflikt | - Speicherplatz fast voll - E-Mail Versand gesperrt - Domain unverifiziert - Zertifikat konnte nicht ausgestellt werden |
| Danger | Kritisches Problem, das sofortiges Handeln erfordert | - Speicherplatz voll - E-Mail Empfang deaktiviert - Webserver/Cronjobs deaktiviert |
| Success | Erfolgreich abgeschlossene Ereignisse | - Erfolgreich installiert - Erfolgreich angelegt - Erfolgreich erstellt - Domain verifiziert |
| Unavailable | Nicht verfügbare Elemente | - Fehlende Berechtigungen - Gelöschtes Objekt |
---
# Alert
Ein [Alert](https://flow.mittwald.de/components/status/alert) wird dort eingesetzt, wo unmittelbare
Aufmerksamkeit erforderlich ist – etwa bei Systemfehlern oder wichtigen
Systeminformationen. Er übermittelt die Information in wenigen Sätzen und gibt,
wenn möglich, eine Handlungsempfehlung. Diese kann mithilfe eines
[Links](https://flow.mittwald.de/components/navigation/link) oder eines
[Buttons](https://flow.mittwald.de/components/actions/button) unterstützt werden. Alerts sollten stets
in der Nähe des betroffenen Inhalts platziert werden (s.
[Alert: Position](https://flow.mittwald.de/components/status/alert#position)). Ein seitenweiter Alert
steht in der
[LayoutCard](https://flow.mittwald.de/components/structure/layout-card#kombiniere-mit-alert) vor der
ersten Section und nicht oberhalb der LayoutCard.
```tsx
import {
Alert,
Heading,
Text,
Content,
Button,
Link,
} from "@mittwald/flow-react-components";
<>
E-Mail-Adresse wurde archiviert
Da deine Domain gelöscht wurde, wurde diese
E-Mail-Adresse archiviert. Um E-Mails empfangen und
senden zu können musst du die Adresse wieder
umbenennen.
Dein Speicher ist fast voll
Dein Speicher ist zu über 80% belegt. Wir empfehlen
dir deinen Speicherplatz zu erweitern, um Problemen
vorzubeugen.
Es konnte kein SSL-Zertifikat ausgestellt werden
Für diese Domain konnte kein SSL-Zertifikat
ausgestellt werden, da die Domain per IP nicht auf
deine Server-IP zeigt.
App aktualisiert
Deine App wurde erfolgreich auf die neueste Version
aktualisiert.
Mehr Informationen zur aktuellen Version
Inhalte nicht gefunden
Die gewünschten Inhalte sind leider nicht verfügbar.
Entweder wurden sie inzwischen gelöscht oder dir
fehlen die notwendigen Berechtigungen.
>
```
---
# AlertBadge
Eine [AlertBadge](https://flow.mittwald.de/components/status/alert-badge) dient dazu, den Status eines
Elements schnell sichtbar zu machen, beispielsweise in
[Headings](https://flow.mittwald.de/components/content/heading) oder
[List](https://flow.mittwald.de/components/list/list)-Einträgen. In vielen Fällen wird sie durch einen
[Alert](https://flow.mittwald.de/components/status/alert) ergänzt, etwa auf der
[Detailseite](https://flow.mittwald.de/templates/seiten/detailseite), um zusätzliche Informationen
bereitzustellen. Für Metadaten oder ergänzende Informationen ist hingegen die
[Badge](https://flow.mittwald.de/components/status/badge) besser geeignet (siehe
[AlertBadge vs. Badge](https://flow.mittwald.de/components/status/alert-badge#best-practices-alertbadge-vs-badge)).
```tsx
import { AlertBadge } from "@mittwald/flow-react-components";
<>
Update verfügbar
Speicherplatz fast voll
Speicherplatz voll
Verifiziert
Nicht gefunden
>
```
---
# Notifications
[Notifications](https://flow.mittwald.de/components/status/notification) informieren über wichtige
System- oder Statusmeldungen und erscheinen kurz im Bildschirm des Users. So
stellen sie sicher, dass User über einen Status informiert werden, auch wenn sie
sich gerade nicht auf der betroffenen
[Detailseite](https://flow.mittwald.de/templates/seiten/detailseite) befinden. Notifications sollten
bewusst und sparsam eingesetzt werden. Denn zu viele Notifications überfluten
den User und lenken von wirklich wichtigen Informationen ab.
Notifications werden durch den
[NotificationsProvider](https://flow.mittwald.de/components/status/notification-provider) ausgelöst. Da
Notifications in der Regel nur kurz sichtbar sind, sollten sie an einem
zentralen Ort gesammelt werden, damit ungelesene Notifications auch zu einem
späteren Zeitpunkt eingesehen werden können. Kritische Probleme sollten
zusätzlich zur Notification per E-Mail kommuniziert werden. So wird
sichergestellt, dass der User die Informationen zuverlässig erhält.
```tsx
import {
Heading,
Notification,
Text,
} from "@mittwald/flow-react-components";
<>
E-Mail-Adresse archiviert
Die E-Mail-Adresse example@mittwald.de wurde
archiviert.
Speicher fast voll
Der Speicherplatz im Projekt My Project ist zu
80% voll.
Kein SSL-Zertifikat
Für exmaple.de konnte kein SSL-Zertifikat
ausgestellt werden.
App installiert
Deine App My WordPress wurde erfolgreich
installiert.
Nicht gefunden
Die gewünschten Inhalte sind leider nicht verfügbar.
>
```
---
# Status-Icon
Einzelne [Status-Icons](https://flow.mittwald.de/components/content/icon#icon-bibliothek) können den
User auf versteckte Status-Informtionen aufmerksam machen. Zum Beispiel zeigt
ein Status-Icon innerhalb von einem [Tab](https://flow.mittwald.de/components/structure/tabs) an, dass
in diesem ein relevanter Status vorliegt. Der Status kann dann im Tab z. B. mit
Hilfe eines [Alerts](https://flow.mittwald.de/components/status/alert) erläutert werden.
```tsx
import {
Tabs,
Tab,
TabTitle,
AlertIcon,
} from "@mittwald/flow-react-components";
Tab
Tab Content
Tab
Tab Content
Tab
Tab Content
```
---
# Fehlermeldungen
Der beste Fehler ist der, der gar nicht erst auftritt. Klare Strukturen,
verständliche Beschriftungen und sinnvolle Defaults helfen, Fehlbedienungen zu
vermeiden. Lassen sich Fehler nicht verhindern, entscheiden zwei Dinge darüber,
ob Nutzer weiterkommen: wo und mit welcher Komponente der Fehler erscheint und
wie er formuliert ist.
---
# Best Practices
- Vermeide Fehler schon im Design, zum Beispiel durch klare Beschriftungen und
sinnvolle Defaults.
- Zeige Fehler dort, wo sie entstehen oder wo der erwartete Inhalt wäre.
- Nenne eine Lösung oder biete einen Ausweg an, zum Beispiel "Erneut laden".
- Wähle die Darstellung passend zum Umfang des Fehlers.
- Bleibe sachlich: keine Schuldzuweisungen und keine Entschuldigungen.
- Formuliere kurz und positiv. Das hilft Nutzern, schnell zu handeln.
---
# Fehlermeldungen anzeigen
## Beim Laden von Inhalten
Beim Laden können Fehler auftreten, zum Beispiel wenn ein Service nicht
erreichbar ist oder ein Timeout entsteht. Beende in diesem Fall den Ladezustand,
damit Nutzer sehen, dass sie nicht weiter warten müssen.
Die Fehlermeldung erscheint dort, wo der erwartete Inhalt wäre. Für größere
Bereiche wie [LayoutCards](https://flow.mittwald.de/components/structure/layout-card) oder
[Sections](https://flow.mittwald.de/components/structure/section) eignet sich eine
[IllustratedMessage](https://flow.mittwald.de/components/content/illustrated-message) mit
`status="danger"`. Sie zeigt deutlich, dass der Inhalt aktuell nicht verfügbar
ist, und bietet idealerweise eine Handlung an, zum Beispiel einen
[Button](https://flow.mittwald.de/components/actions/button) mit der Beschriftung "Erneut laden".
```tsx
import {
Flex,
Heading,
IconDanger,
IllustratedMessage,
LayoutCard,
Text,
} from "@mittwald/flow-react-components";
export default () => {
return (
Mein Projekt
Fehler beim Laden von Daten
Dieser Bereich konnte nicht geladen werden. Wir
arbeiten daran das Problem zu beheben. Bitte
habe etwas Geduld und probiere es später noch
einmal.
);
}
```
Für kleinere, abgegrenzte Elemente wie Tabellen- oder Listenzeilen eignet sich
ein [AlertText](https://flow.mittwald.de/components/status/alert-text). So bleibt der Kontext erhalten
und der Fehler betrifft nicht die ganze [Table](https://flow.mittwald.de/components/structure/table)
oder [List](https://flow.mittwald.de/components/list/list).
```tsx
import {
AlertText,
Avatar,
Content,
Flex,
Heading,
IconDomain,
LayoutCard,
ListItemView,
Text,
typedList,
} from "@mittwald/flow-react-components";
export default () => {
const DomainList = typedList<{
id: string;
domain: string;
target?: string;
}>();
return (
Domains
domain.id}
loadingItemsCount={3}
>
domain.domain}
>
{(domain) => (
{domain.domain}
{domain.target ? (
domain.target
) : (
Fehler beim Laden
)}
)}
);
}
```
Um Fehler auf kleine Bereiche zu begrenzen, nutzen wir im mStudio Loading und
Error Boundaries. Sie kapseln technische Fehler ab und sorgen für konsistentes
Feedback.
## Bei ungültigen Eingaben
Zeige das Feedback direkt am Form Control, damit Nutzer sofort erkennen, was sie
anpassen müssen. Details stehen unter
[Formular (Mit Validierung)](https://flow.mittwald.de/templates/bausteine/formular#varianten-und-abwandlung-mit-validierung).
## Bei fehlgeschlagenen Aktionen
Aktionen, die einen Request an das Backend senden, können aus technischen
Gründen, wegen Kommunikationsproblemen oder fehlender Berechtigungen
fehlschlagen. Dafür eignet sich ein [Modal](https://flow.mittwald.de/components/overlays/modal) mit
einer [IllustratedMessage](https://flow.mittwald.de/components/content/illustrated-message) mit
`status="danger"`. Sie erklärt kurz den Grund, sofern er für Nutzer relevant
ist, und bietet einen Ausweg an, zum Beispiel die Aktion erneut auszuführen.
```tsx
import {
Action,
ActionGroup,
Button,
Content,
Flex,
Heading,
IconDanger,
IconProject,
IllustratedMessage,
LayoutCard,
Modal,
Text,
useModalController,
} from "@mittwald/flow-react-components";
export default () => {
const controller = useModalController();
return (
Projekte
Erstes Projekt anlegen
Du hast noch keine Projekte angelegt! Lege ein
Projekt an und schon kannst du mit dem
Entwickeln loslegen.
{
controller.open();
}}
>
Fehler beim Ausführen der Aktion
Die Aktion konnte nicht ausgeführt werden.
Wir arbeiten daran das Problem zu beheben.
Bitte habe etwas Geduld und probiere es
später noch einmal.
);
}
```
Schlägt eine Aktion erst später fehl, nachdem sie zunächst erfolgreich an das
Backend übermittelt wurde, eignen sich
[Notifications](https://flow.mittwald.de/components/status/notification) besser. Ein typisches Beispiel
ist das Registrieren einer Domain. Mehr dazu steht im
[Informationskonzept](https://flow.mittwald.de/foundations/content-guidelines/informationskonzept).
---
# Fehlermeldungen formulieren
Die allgemeinen Regeln zu Anrede und Satzlänge stehen im
[Sprach-Guide](https://flow.mittwald.de/foundations/content-guidelines/sprach-guide). Dort steht auch,
dass eine passive Schreibweise in Ordnung ist, wenn ein System handelt. Das
trifft bei Fehlermeldungen oft zu. Für sie gilt zusätzlich:
## Kurz formulieren und Lösung nennen
Schreibe so kurz wie möglich, aber so lang wie nötig. Oft reicht es, nur die
Lösung zu nennen. Gibt es mehrere mögliche Ursachen, beschreibe, wie sich die
wahrscheinlichste beheben lässt.
**✅ Do**
**⛔️ Don't**
Wir unterstützen die Formatierung nicht. Gib eine E-Mail-Adresse im Format
name@domain.de ein.
## Schuldzuweisungen vermeiden
Beschreibe, was passiert ist, und nicht, was der Nutzer falsch gemacht hat. Ein
Fehler ist an sich schon frustrierend.
**✅ Do**
Das Bild konnte nicht hochgeladen werden. Wähle eine Datei bis 10 MB.
**⛔️ Don't**
Du hast ein ungültiges Bild hochgeladen. Wähle eine Datei bis 10 MB.
## Nicht entschuldigen
Formuliere Fehlermeldungen sachlich und fachlich. Entschuldigungen lenken vom
Wesentlichen ab und lassen Fehler größer wirken.
**✅ Do**
Die Eingabe konnte nicht gespeichert werden. Versuche es erneut.
**⛔️ Don't**
Es tut uns leid, wir konnten deine Eingabe nicht speichern. Versuche es erneut.
## Positiv formulieren
Konzentriere dich auf die Lösung und darauf, was als Nächstes möglich ist.
Verzichte nach Möglichkeit auf negativ besetzte Begriffe wie Fehler, Problem,
fehlgeschlagen, falsch oder ungültig. Bei Systemfehlern ohne direkte Lösung
gelingt das nicht immer.
**⛔️ Don't**
Durch ein Problem mit dem Server ist die Verbindung fehlgeschlagen.
## Allgemein formulieren
Formuliere eher allgemein als zu spezifisch. Details wie Dateinamen oder Ordner
finden Nutzer meist an anderer Stelle in der Oberfläche.
**✅ Do**
Die Datei konnte nicht hochgeladen werden. Verwende eine Datei vom Typ XY.
**⛔️ Don't**
Die Datei „example_file-20.4.24.xy“ konnte nicht hochgeladen werden. Verwende
eine Datei vom Typ XY.
---
# Focus Task
**focus-task.tsx**
```tsx
import {
Button,
Flex,
Heading,
Label,
LayoutCard,
Link,
Section,
Text,
TextField,
} from "@mittwald/flow-react-components";
import styles from "./focus-task.module.css";
export default () => (
Login
Passwort vergessen
Du hast noch keinen Nutzer?{" "}
Registrieren
Datenschutz
Impressum
)
```
**focus-task.module.css**
```css
.page {
/* Container-relative here; use 100dvh in a real full-page app. */
height: 100%;
background: var(--texture--grain), var(--color--background-gradient);
background-blend-mode: overlay, normal;
}
.center {
width: 100%;
max-width: 30rem;
}
/*
* The wordmark rides on `currentColor` through a CSS mask, so it follows the
* theme (dark on light, light on dark). Swap the masked SVG for your own logo.
*/
.logo {
align-self: center;
width: 18rem;
max-width: 100%;
aspect-ratio: 300 / 57;
background: currentcolor;
mask: url("./mittwald.svg") no-repeat center / contain;
-webkit-mask: url("./mittwald.svg") no-repeat center / contain;
}
.registerLine {
text-align: end;
}
/* The generous page padding is too much for a narrow screen. */
@media (max-width: 768px) {
.page {
padding-inline: var(--size-px--m);
}
}
```
---
# Verwendung
Ein Focus Task ist eine einzelne Seite ohne umgebende Navigation. Verwende ihn
für einen klar abgegrenzten Vorgang wie Registrierung, Anmeldung, Konfiguration
oder Bestellung. Ohne Navigation bleibt der Nutzer auf die eine Aufgabe
konzentriert und schließt sie zielgerichtet ab.
---
# Aufbau
- **Content** – Die Aufgabe liegt in einer
[LayoutCard](https://flow.mittwald.de/components/structure/layout-card): Eingaben und Aktionen stehen
direkt darin, ohne umgebende Navigation.
- **Footer** – Hier lassen sich [Links](https://flow.mittwald.de/components/navigation/link) platzieren,
etwa Verweise auf Datenschutz oder Impressum.
---
# Varianten und Abwandlung
## Konfigurations-Aufgabe
Auch eine mehrteilige Aufgabe bleibt ein Focus Task, solange sie ohne Navigation
auf ein Ziel führt. Über die [ActionGroup](https://flow.mittwald.de/components/actions/action-group)
gelangt der Nutzer zum jeweils nächsten Schritt.
**focus-task-bestellung.tsx**
```tsx
import {
Badge,
Button,
ColumnLayout,
Content,
Flex,
Heading,
Label,
LayoutCard,
Link,
RadioButton,
RadioGroup,
Section,
Table,
TableBody,
TableCell,
TableColumn,
TableFooterRow,
TableHeader,
TableRow,
Text,
TextField,
} from "@mittwald/flow-react-components";
import styles from "./focus-task-bestellung.module.css";
export default () => (
BestellungIndividuell konfigurieren
Passe deinen Tarif ganz einfach an deine
Bedürfnisse an. Wähle Ressourcen und
Speicherplatz so, wie du sie wirklich
brauchst, und stelle dir deinen passenden
Tarif zusammen.
Ressourcen
Die Ressourcen aus vCPU und RAM bestimmen die
Leistung deines Tarifs. Du kannst sie
jederzeit nach der Buchung anpassen.
1 vCPU1 GiB RAM1 vCPUEmpfehlung2 GiB RAM2 vCPU4 GiB RAMSpeicherplatz
Der Speicherplatz umfasst Datenbanken,
Backups, Webspace und E-Mails aller Projekte
auf deinem Tarif.
20 GiB40 GiB60 GiB80 GiB100 GiB
Individuell
Kostenübersicht
Datenschutz
Impressum
)
```
**focus-task-bestellung.module.css**
```css
.page {
/* Container-relative here; use 100dvh in a real full-page app. */
min-height: 100%;
background: var(--texture--grain), var(--color--background-gradient);
background-blend-mode: overlay, normal;
}
.center {
width: 100%;
max-width: 84rem;
}
/*
* The wordmark rides on `currentColor` through a CSS mask, so it follows the
* theme. Swap the masked SVG for your own logo.
*/
.logo {
align-self: flex-start;
width: 12rem;
max-width: 100%;
aspect-ratio: 300 / 57;
background: currentcolor;
mask: url("./mittwald.svg") no-repeat center / contain;
-webkit-mask: url("./mittwald.svg") no-repeat center / contain;
}
/*
* The RadioButton lays its slots out on a grid ("icon label" / ". content").
* A raw child lands in the wrong cell, so the title row claims the label area.
*/
.resourceTitle {
grid-area: label;
}
.actionsRight {
margin-inline-start: auto;
}
/* The generous page padding is too much for a narrow screen. */
@media (max-width: 768px) {
.page {
padding-inline: var(--size-px--m);
}
}
```
---
# Simple App
**simple-app.tsx**
```tsx
import {
ActionGroup,
Avatar,
Button,
CartesianChart,
ChartGrid,
ChartLegend,
ChartTooltip,
ColumnLayout,
Combine,
Content,
ContextMenu,
ContextMenuTrigger,
CounterBadge,
DonutChart,
Flex,
Header,
HeaderNavigation,
Heading,
Icon,
IconLogout,
IconMenu,
IconNotification,
IconSettings,
Initials,
LayoutCard,
Line,
Link,
MenuItem,
Message,
MessageThread,
Modal,
ModalTrigger,
Navigation,
Rating,
Section,
Text,
XAxis,
YAxis,
} from "@mittwald/flow-react-components";
import {
IconTrendingDown,
IconTrendingUp,
} from "@tabler/icons-react";
import styles from "./simple-app.module.css";
const contracts = [
{
month: "Jan",
Webhosting: 33,
vServer: 72,
"Dedicated Server": 52,
},
{
month: "Feb",
Webhosting: 52,
vServer: 84,
"Dedicated Server": 80,
},
{
month: "Mär",
Webhosting: 62,
vServer: 83,
"Dedicated Server": 98,
},
{
month: "Apr",
Webhosting: 24,
vServer: 63,
"Dedicated Server": 42,
},
{
month: "Mai",
Webhosting: 70,
vServer: 78,
"Dedicated Server": 50,
},
{
month: "Jun",
Webhosting: 96,
vServer: 88,
"Dedicated Server": 54,
},
{
month: "Jul",
Webhosting: 146,
vServer: 98,
"Dedicated Server": 56,
},
{
month: "Aug",
Webhosting: 132,
vServer: 85,
"Dedicated Server": 90,
},
{
month: "Sep",
Webhosting: 114,
vServer: 82,
"Dedicated Server": 148,
},
{
month: "Okt",
Webhosting: 18,
vServer: 42,
"Dedicated Server": 120,
},
{
month: "Nov",
Webhosting: 48,
vServer: 70,
"Dedicated Server": 98,
},
{
month: "Dez",
Webhosting: 68,
vServer: 110,
"Dedicated Server": 126,
},
];
export default () => (
{/* The active area is already named in the navigation, so the page
heading is only exposed to assistive technology. */}
Dashboard
Neuverträge
+ 2.000 €
Vertragsbestand (30 Tage)
40
Neuverträge (30 Tage)
Vertragszusammensetzung1,2 KLetzte Kundenfeedbacks
Datenschutz
Impressum
);
/*
* The bell keeps its place next to the burger: a pending notification is worth
* seeing without opening the menu first.
*/
const NotificationButton = () => (
);
/*
* The navigation links, shared by the top bar and the off-canvas. Each renders
* them through its own props context, so the same Links read as a header bar
* in one and as a navigation list in the other.
*/
const GlobalNavigationLinks = () => (
<>
Dashboard
Kündigungen
Vertragsbestand
>
);
/*
* On a narrow screen the top bar has no room for the navigation, so it moves
* in here. The burger is then the shell's only navigation control.
*/
const MobileMenu = () => (
Menü
);
interface FeedbackProps {
name: string;
date: string;
rating: number;
text: string;
}
const Feedback = ({
name,
date,
rating,
text,
}: FeedbackProps) => (
{name}{name}{date}{text}
)
```
**simple-app.module.css**
```css
.app {
/* Header and content share one maximum width, so their edges line up. */
--page-width: 1800px;
/* The header's tint is mixed from the gradient's first stop. */
--header-ground: var(--neutral--color--200);
/* Container-relative here; use 100dvh in a real full-page app. */
min-height: 100%;
background: var(--texture--grain), var(--color--background-gradient);
background-blend-mode: overlay, normal;
}
/*
* The header stays at the top while the page scrolls beneath it. Its tint is
* translucent and blurred, so the navigation stays legible above the content.
*/
.header {
position: sticky;
top: 0;
z-index: 1;
padding-block: var(--size-px--m);
background-color: color-mix(in srgb, var(--header-ground) 40%, transparent);
backdrop-filter: blur(var(--blur));
}
.headerContent {
max-width: var(--page-width);
margin-inline: auto;
padding-inline: var(--size-px--xl);
}
.content {
width: 100%;
max-width: var(--page-width);
margin-inline: auto;
padding-block: var(--size-px--s) var(--size-px--l);
padding-inline: var(--size-px--xl);
}
/*
* The wordmark rides on `currentColor` through a CSS mask, so it follows the
* theme. Swap the masked SVG for your own logo.
*/
.logo {
width: 9rem;
max-width: 100%;
aspect-ratio: 300 / 57;
background: currentcolor;
mask: url("./mittwald.svg") no-repeat center / contain;
-webkit-mask: url("./mittwald.svg") no-repeat center / contain;
}
/* The navigation is pushed to the far end of the top bar. */
.nav {
margin-inline-start: auto;
}
/* The bell and the burger take that place once the navigation gives it up. */
.mobileActions {
margin-inline-start: auto;
}
/* Keeps the heading in the accessibility tree while hiding it visually. */
.visuallyHidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
/* A single metric: trend icon, the value, and its caption. */
.stat {
text-align: center;
}
.statValue {
font-size: 1.75rem;
font-weight: var(--font-weight--bold, 700);
}
.statLabel {
font-weight: var(--font-weight--medium);
}
.footer {
margin-block-start: auto;
}
/*
* Below this width the top bar has no room for the navigation, so it hands its
* links to the off-canvas behind the burger.
*/
@media (max-width: 768px) {
.nav {
display: none;
}
.headerContent,
.content {
padding-inline: var(--size-px--m);
}
}
@media (min-width: 769px) {
.mobileActions {
display: none;
}
}
@media (prefers-contrast: more), (prefers-reduced-transparency) {
.header {
background-color: var(--header-ground);
backdrop-filter: none;
}
}
/* The dark gradient starts darker than the neutral token. */
[data-theme="dark"] .app {
--header-ground: #121518;
}
```
---
# Verwendung
Eine Simple App führt wenige gleichrangige Bereiche unter einer einzigen,
flachen Navigationsebene zusammen. Verwende sie für ein überschaubares Werkzeug,
dessen Bereiche alle auf einer Ebene liegen. Die flache Struktur macht jeden
Bereich mit einem Klick erreichbar. Sobald eine zweite Navigationsebene nötig
wird, ist es eine [Complex App](https://flow.mittwald.de/templates/app-shells/complex-app).
---
# Aufbau
- **Header** – Eine [HeaderNavigation](https://flow.mittwald.de/components/navigation/header-navigation)
bildet die einzige, flache Navigationsebene über die gleichrangigen Bereiche
und bietet Raum für bereichsübergreifende Funktionen wie eine Suche oder die
Profilverwaltung. Er bleibt beim Scrollen oben stehen, der Inhalt läuft
darunter durch.
- **Content** – Der Inhalt ist auf eine maximale Breite begrenzt und wird in der
Regel in [LayoutCards](https://flow.mittwald.de/components/structure/layout-card) dargestellt.
Typische Seitentypen sind beispielsweise
[Detailseiten](https://flow.mittwald.de/templates/seiten/detailseite),
[Dashboards](https://flow.mittwald.de/templates/seiten/dashboard) und
[Übersichtsseiten](https://flow.mittwald.de/templates/seiten/uebersichtsseite).
- **Footer** – Hier lassen sich [Links](https://flow.mittwald.de/components/navigation/link) platzieren,
etwa Verweise auf Datenschutz oder Impressum.
Auf schmalen Screens ist für die Header-Navigation kein Platz. Sie gibt ihre
Links an ein [OffCanvas](https://flow.mittwald.de/components/overlays/modal) ab, das ein Burger-Menü in
der Kopfleiste öffnet.
---
# Varianten und Abwandlung
## Mit seitlicher Navigation
Wenn der Header nicht genug Platz für alle gleichrangigen Bereiche bietet,
wandert die flache Navigation nach links in eine
[Navigation](https://flow.mittwald.de/components/navigation/navigation). Sie nimmt auch das Logo,
globale Funktionen wie Benachrichtigungen und Einstellungen sowie das
Nutzerprofil auf, sodass der Header ganz entfällt. Die Struktur bleibt einstufig
und damit eine Simple App. Öffnet ein Wechsel je Bereich eine eigene
Seitennavigation, entsteht eine
[Complex App](https://flow.mittwald.de/templates/app-shells/complex-app).
**simple-app-sidebar.tsx**
```tsx
import {
Avatar,
Badge,
Button,
ColumnLayout,
Combine,
CounterBadge,
Flex,
Heading,
IconCustomer,
IconDashboard,
IconInvoice,
IconLogout,
IconNotification,
IconPayment,
IconQuote,
IconSettings,
Initials,
LayoutCard,
Link,
Navigation,
Section,
Text,
typedList,
} from "@mittwald/flow-react-components";
import styles from "./simple-app-sidebar.module.css";
type Status = "Bezahlt" | "Offen" | "Überfällig";
const statusColor: Record<
Status,
"green" | "neutral" | "red"
> = {
Bezahlt: "green",
Offen: "neutral",
Überfällig: "red",
};
interface Invoice {
number: string;
customer: string;
createdAt: string; // ISO date, sorted on
date: string; // formatted for display
amount: string;
status: Status;
}
// A page of invoices, one per day counting back from 30.09.2026, with enough
// rows to sort, filter and search through.
const invoices: Invoice[] = [
{
number: "RE-2026-0148",
customer: "KD-1298",
createdAt: "2026-09-30",
date: "30.09.2026",
amount: "844,00 €",
status: "Offen",
},
{
number: "RE-2026-0149",
customer: "KD-0553",
createdAt: "2026-09-29",
date: "29.09.2026",
amount: "965,00 €",
status: "Bezahlt",
},
{
number: "RE-2026-0150",
customer: "KD-0987",
createdAt: "2026-09-28",
date: "28.09.2026",
amount: "917,00 €",
status: "Bezahlt",
},
{
number: "RE-2026-0151",
customer: "KD-0553",
createdAt: "2026-09-27",
date: "27.09.2026",
amount: "844,00 €",
status: "Überfällig",
},
{
number: "RE-2026-0152",
customer: "KD-1298",
createdAt: "2026-09-26",
date: "26.09.2026",
amount: "418,00 €",
status: "Überfällig",
},
{
number: "RE-2026-0153",
customer: "KD-0553",
createdAt: "2026-09-25",
date: "25.09.2026",
amount: "1.434,00 €",
status: "Offen",
},
{
number: "RE-2026-0154",
customer: "KD-0553",
createdAt: "2026-09-24",
date: "24.09.2026",
amount: "1.559,00 €",
status: "Offen",
},
{
number: "RE-2026-0155",
customer: "KD-0765",
createdAt: "2026-09-23",
date: "23.09.2026",
amount: "2.032,00 €",
status: "Überfällig",
},
{
number: "RE-2026-0156",
customer: "KD-0987",
createdAt: "2026-09-22",
date: "22.09.2026",
amount: "2.269,00 €",
status: "Bezahlt",
},
{
number: "RE-2026-0157",
customer: "KD-1298",
createdAt: "2026-09-21",
date: "21.09.2026",
amount: "1.230,00 €",
status: "Offen",
},
{
number: "RE-2026-0158",
customer: "KD-1042",
createdAt: "2026-09-20",
date: "20.09.2026",
amount: "2.771,00 €",
status: "Offen",
},
{
number: "RE-2026-0159",
customer: "KD-0987",
createdAt: "2026-09-19",
date: "19.09.2026",
amount: "2.556,00 €",
status: "Offen",
},
{
number: "RE-2026-0160",
customer: "KD-1298",
createdAt: "2026-09-18",
date: "18.09.2026",
amount: "669,00 €",
status: "Bezahlt",
},
{
number: "RE-2026-0161",
customer: "KD-1042",
createdAt: "2026-09-17",
date: "17.09.2026",
amount: "1.761,00 €",
status: "Bezahlt",
},
{
number: "RE-2026-0162",
customer: "KD-0553",
createdAt: "2026-09-16",
date: "16.09.2026",
amount: "871,00 €",
status: "Überfällig",
},
];
export default () => {
const InvoiceList = typedList();
return (
{/* The current user sits at the very bottom of the sidebar. */}
Max MustermannMax Mustermann
{/* The active area is already named in the navigation, so the page
heading is only exposed to assistive technology. */}
Rechnungen
invoice.number}
>
Rechnungsnummer
Kundennummer
Ausstellungsdatum
Betrag
Status
{(invoice) => invoice.number}
{(invoice) => invoice.customer}
{(invoice) => invoice.date}
{(invoice) => invoice.amount}
{(invoice) => (
{invoice.status}
)}
Datenschutz
Impressum
);
};
/*
* The flat list of equal-ranking areas — the single navigation level of a
* Simple App, moved from the header into the sidebar.
*/
const AreaNavigation = () => (
DashboardRechnungenAngeboteKundenZahlungseingang
);
/*
* Supplementary points that would be out of place among the areas. They sit
* apart from the flat list above.
*/
const AdminNavigation = () => (
BenachrichtigungenEinstellungen
)
```
**simple-app-sidebar.module.css**
```css
/* The container the page-scroll switch below is measured against. */
.shell {
container-type: inline-size;
height: 100%;
background: var(--texture--grain), var(--color--background-gradient);
background-blend-mode: overlay, normal;
}
.app {
/* Fixed to the viewport height (container-relative here; use 100dvh in a real
full-page app) so the cards below can fill it and scroll their own content
instead of growing the page. */
height: 100%;
max-width: 1800px;
margin-inline: auto;
overflow: hidden;
}
/*
* The wordmark rides on `currentColor` through a CSS mask, so it follows the
* theme. Swap the masked SVG for your own logo.
*/
.logo {
width: 9rem;
max-width: 100%;
aspect-ratio: 300 / 57;
/* Extra breathing room between the wordmark and the navigation below it. */
margin-block-end: var(--size-px--m);
background: currentcolor;
mask: url("./mittwald.svg") no-repeat left center / contain;
-webkit-mask: url("./mittwald.svg") no-repeat left center / contain;
}
/* Sidebar and content fill the available height side by side; the column
layout stacks them once its container gets narrow. */
.body {
flex: 1;
min-height: 0;
}
/* The column layout's inner grid fills that height, so its row stretches. */
.body > * {
height: 100%;
}
/* The navigation card scrolls its own content if the viewport is too short for
it. */
.sidebar {
align-self: stretch;
gap: var(--size-px--m);
min-height: 0;
overflow: auto;
}
/* Settings and the user profile sit at the bottom, apart from the areas above. */
.sidebarBottom {
margin-block-start: auto;
}
.main {
align-self: stretch;
min-width: 0;
min-height: 0;
}
/* The content card grows with its content, but only up to the viewport: once it
would run past the bottom it stops there and scrolls its own overflow (the
long invoice list) instead of growing the page. */
.contentCard {
flex: 0 1 auto;
min-height: 0;
overflow: auto;
}
/* Keeps the heading in the accessibility tree while hiding it visually. */
.visuallyHidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
.footer {
margin-block-start: auto;
}
/*
* Once the column layout stacks the cards (its content box at or below 850px,
* i.e. 850px plus the xl side padding), the page scrolls as a whole rather than
* each card scrolling on its own inside a fixed height.
*/
@container (max-width: 914px) {
.app {
height: auto;
overflow: visible;
}
.sidebar {
overflow: visible;
}
.main {
min-height: auto;
}
.contentCard {
flex: none;
min-height: auto;
overflow: visible;
}
}
@media (max-width: 768px) {
.app {
padding-inline: var(--size-px--m);
}
}
```
---
# Complex App
**complex-app.tsx**
```tsx
import {
ActionGroup,
Avatar,
Breadcrumb,
Button,
Content,
ContextMenu,
ContextMenuTrigger,
CounterBadge,
Flex,
HeaderNavigation,
Heading,
IconApp,
IconCronjob,
IconDashboard,
IconDatabase,
IconDomain,
IconEmail,
IconLogout,
IconMember,
IconMenu,
IconMonitoring,
IconNotification,
IconSearch,
IconSettings,
IconSshSftp,
Initials,
Label,
LayoutCard,
Link,
MenuItem,
Modal,
ModalTrigger,
Navigation,
NavigationGroup,
ProgressBar,
Section,
Text,
typedList,
} from "@mittwald/flow-react-components";
import styles from "./complex-app.module.css";
interface Mailbox {
id: string;
address: string;
type: string;
color: "blue" | "teal" | "violet";
storage?: { used: number; max: number };
}
const mailboxes: Mailbox[] = [
{
id: "1",
address: "max.mustermann@mittwald.de",
type: "E-Mail-Adresse",
color: "blue",
storage: { used: 1, max: 2 },
},
{
id: "2",
address: "kontakt@mittwald.de",
type: "E-Mail-Adresse",
color: "blue",
storage: { used: 0.5, max: 2 },
},
{
id: "3",
address: "info@mittwald.de",
type: "Weiterleitungsadresse",
color: "teal",
},
{
id: "4",
address: "hallo@mittwald.de",
type: "Catch-All-Adresse",
color: "violet",
storage: { used: 0, max: 2 },
},
];
const MailboxList = typedList();
export default () => (
Projekt
E-Mails
E-Mails m.id}
>
m.address}
>
{(m) => (
{m.address}{m.type}
{m.storage && (
)}
)}
);
/*
* The global navigation links, shared by the top bar and the off-canvas. Each
* renders them through its own props context, so the same Links read as a
* header bar in one and as a navigation list in the other.
*/
const GlobalNavigationLinks = () => (
<>
Dashboard
Organisation
Server
Projekte
>
);
const Topbar = () => (
);
/*
* Search and the bell keep their place next to the burger: both are worth
* reaching without opening the menu first.
*/
const SearchButton = () => (
);
const NotificationButton = () => (
);
/*
* On a narrow screen the top bar has no room for the navigation and the
* sidebar has no column of its own, so both move in here. The burger is then
* the shell's only navigation control.
*/
const MobileMenu = () => (
MenüMein Projekt
);
const ProjectSidebar = () => (
Mein Projekt
);
const ProjectNavigation = () => (
DashboardMonitoringAppsDomainsE-MailsDatenbankenCronjobsSSH/SFTPMitgliederEinstellungen
);
const Footer = () => (
Datenschutz
Impressum
)
```
**complex-app.module.css**
```css
.app {
/* Header and content share one maximum width, so their edges line up. */
--page-width: 1800px;
/* The header's tint is mixed from the gradient's first stop. */
--header-ground: var(--neutral--color--200);
/* The header's rendered height; the sticky sidebar starts just below it. */
--header-height: 72px;
/* Container-relative here; use 100dvh in a real full-page app. */
min-height: 100%;
background: var(--texture--grain), var(--color--background-gradient);
background-blend-mode: overlay, normal;
}
/*
* The header stays at the top while the page scrolls beneath it. Its tint is
* translucent and blurred, so the navigation stays legible above the content.
*/
.header {
position: sticky;
top: 0;
z-index: 1;
padding-block: var(--size-px--m);
background-color: color-mix(in srgb, var(--header-ground) 40%, transparent);
backdrop-filter: blur(var(--blur));
}
.headerContent {
max-width: var(--page-width);
margin-inline: auto;
padding-inline: var(--size-px--xl);
}
.content {
width: 100%;
max-width: var(--page-width);
margin-inline: auto;
padding-block: var(--size-px--s) var(--size-px--l);
padding-inline: var(--size-px--xl);
}
/*
* The wordmark rides on `currentColor` through a CSS mask, so it follows the
* theme. Swap the masked SVG for your own logo.
*/
.logo {
width: 7rem;
max-width: 100%;
aspect-ratio: 300 / 57;
background: currentcolor;
mask: url("./mittwald.svg") no-repeat center / contain;
-webkit-mask: url("./mittwald.svg") no-repeat center / contain;
}
/* The global navigation is pushed to the far end of the top bar. */
.topnav {
margin-inline-start: auto;
}
/* The bell and the burger take that place once the navigation gives it up. */
.mobileActions {
margin-inline-start: auto;
}
/* Sidebar and content fill the space between top bar and footer. */
.body {
flex: 1;
min-height: 0;
}
/*
* The navigation card keeps a fixed width beside the flexible content. It stays
* below the header while the page scrolls, and scrolls its own links once they
* outgrow the viewport.
*/
.sidebar {
flex: 0 0 17rem;
position: sticky;
top: calc(var(--header-height) + var(--size-px--s));
align-self: flex-start;
max-height: calc(
100dvh - var(--header-height) - var(--size-px--s) - var(--size-px--l)
);
overflow: auto;
}
.main {
flex: 1 1 auto;
min-width: 0;
}
.footer {
margin-block-start: auto;
}
/*
* Below this width neither the global navigation nor the sidebar fits, so both
* hand their links to the off-canvas behind the burger.
*/
@media (max-width: 768px) {
.topnav,
.sidebar {
display: none;
}
.headerContent,
.content {
padding-inline: var(--size-px--m);
}
}
@media (min-width: 769px) {
.mobileActions {
display: none;
}
}
@media (prefers-contrast: more), (prefers-reduced-transparency) {
.header {
background-color: var(--header-ground);
backdrop-filter: none;
}
}
/* The dark gradient starts darker than the neutral token. */
[data-theme="dark"] .app {
--header-ground: #121518;
}
```
---
# Verwendung
Eine Complex App erschließt viele Bereiche über mehrere Navigationsebenen aus
globalem Header, Sidebar und Breadcrumb. Das ist die im mStudio typische Form.
Verwende sie, wenn eine flache Navigation nicht mehr ausreicht. Die gestaffelten
Ebenen halten auch eine große Anwendung übersichtlich und zeigen dem Nutzer
jederzeit, wo er sich befindet. Für ein kleines Werkzeug mit wenigen,
gleichrangigen Bereichen genügt eine
[Simple App](https://flow.mittwald.de/templates/app-shells/simple-app).
---
# Aufbau
- **Header** – Eine [HeaderNavigation](https://flow.mittwald.de/components/navigation/header-navigation)
trägt die globale Navigationsebene und bereichsübergreifende Funktionen. Er
bleibt beim Scrollen oben stehen, der Inhalt läuft darunter durch.
- **Sidebar** – Eine [Navigation](https://flow.mittwald.de/components/navigation/navigation) führt durch
die Unterseiten des aktuellen Bereichs. Sie bleibt beim Scrollen unter dem
Header stehen.
- **Content Header** – [Breadcrumb](https://flow.mittwald.de/components/navigation/breadcrumb) und
PageTitle (ein [Heading](https://flow.mittwald.de/components/content/heading) der Ebene 1) orientieren
den Nutzer in der Tiefe.
- **Content** – Der Inhalt ist auf eine maximale Breite begrenzt und wird in der
Regel in [LayoutCards](https://flow.mittwald.de/components/structure/layout-card) dargestellt.
Typische Seitentypen sind beispielsweise
[Detailseiten](https://flow.mittwald.de/templates/seiten/detailseite),
[Dashboards](https://flow.mittwald.de/templates/seiten/dashboard) und
[Übersichtsseiten](https://flow.mittwald.de/templates/seiten/uebersichtsseite).
- **Footer** – Hier lassen sich [Links](https://flow.mittwald.de/components/navigation/link) platzieren,
etwa Verweise auf Datenschutz oder Impressum.
Auf schmalen Screens ist für Header-Navigation und Sidebar kein Platz. Beide
geben ihre Links an ein [OffCanvas](https://flow.mittwald.de/components/overlays/modal) ab, das ein
Burger-Menü in der Kopfleiste öffnet.
---
# Varianten und Abwandlung
## Detailansicht
Die Detailansicht kombiniert zwei Seitentypen: Über die
[Übersichtsseite](https://flow.mittwald.de/templates/seiten/uebersichtsseite) – eine
[List](https://flow.mittwald.de/components/list/list) – navigiert man in die Tiefe zu einem einzelnen
Objekt. [Breadcrumb](https://flow.mittwald.de/components/navigation/breadcrumb) und PageTitle helfen bei
der Orientierung; eine [TabNavigation](https://flow.mittwald.de/components/navigation/tab-navigation)
gliedert das Objekt in Unterbereiche.
**complex-app-detail.tsx**
```tsx
import {
ActionGroup,
Avatar,
Breadcrumb,
Button,
ColumnLayout,
Content,
ContextMenu,
ContextMenuTrigger,
CounterBadge,
Flex,
Header,
HeaderNavigation,
Heading,
IconApp,
IconCronjob,
IconDashboard,
IconDatabase,
IconDomain,
IconEmail,
IconLogout,
IconMember,
IconMenu,
IconMonitoring,
IconNotification,
IconSearch,
IconSettings,
IconSshSftp,
Initials,
InlineCode,
Label,
LabeledValue,
LayoutCard,
Link,
MenuItem,
Modal,
ModalTrigger,
Navigation,
NavigationGroup,
ProgressBar,
Section,
Switch,
Tab,
TabNavigation,
Tabs,
TabTitle,
Text,
} from "@mittwald/flow-react-components";
import styles from "./complex-app-detail.module.css";
const connections = {
imap: {
server: "imap.mittwald.de",
port: "993 (SSL/TLS)",
},
pop3: {
server: "pop3.mittwald.de",
port: "995 (SSL/TLS)",
},
smtp: {
server: "smtp.mittwald.de",
port: "465 (SSL/TLS)",
},
};
export default () => (
Projekt
E-Mails
E-Mail-Details
max.mustermann@mittwald.de
Allgemein
Weiterleitungen
Autoresponder
E-Mail-Adresse
max.mustermann@mittwald.de
mittwald Webmailer
Speicherplatz
Die E-Mail-Adresse verfügt über
Speicherplatz zum Empfangen und Speichern
von E-Mails. Wir empfehlen mindestens 2 GB.
Verbindungsinformationen
{Object.entries(connections).map(
([key, c]) => (
{key.toUpperCase()}
max.mustermann@mittwald.de
{c.server}
{c.port}
),
)}
Spamschutz
Der Spamfilter schützt dich vor ungewollten
E-Mails. Wir empfehlen, ihn immer aktiviert
zu lassen.
);
/*
* The global navigation links, shared by the top bar and the off-canvas. Each
* renders them through its own props context, so the same Links read as a
* header bar in one and as a navigation list in the other.
*/
const GlobalNavigationLinks = () => (
<>
Dashboard
Organisation
Server
Projekte
>
);
const Topbar = () => (
);
/*
* Search and the bell keep their place next to the burger: both are worth
* reaching without opening the menu first.
*/
const SearchButton = () => (
);
const NotificationButton = () => (
);
/*
* On a narrow screen the top bar has no room for the navigation and the
* sidebar has no column of its own, so both move in here. The burger is then
* the shell's only navigation control.
*/
const MobileMenu = () => (
MenüMein Projekt
);
const ProjectSidebar = () => (
Mein Projekt
);
const ProjectNavigation = () => (
DashboardMonitoringAppsDomainsE-MailsDatenbankenCronjobsSSH/SFTPMitgliederEinstellungen
);
const Footer = () => (
Datenschutz
Impressum
)
```
**complex-app-detail.module.css**
```css
.app {
/* Header and content share one maximum width, so their edges line up. */
--page-width: 1800px;
/* The header's tint is mixed from the gradient's first stop. */
--header-ground: var(--neutral--color--200);
/* The header's rendered height; the sticky sidebar starts just below it. */
--header-height: 72px;
/* Container-relative here; use 100dvh in a real full-page app. */
min-height: 100%;
background: var(--texture--grain), var(--color--background-gradient);
background-blend-mode: overlay, normal;
}
/*
* The header stays at the top while the page scrolls beneath it. Its tint is
* translucent and blurred, so the navigation stays legible above the content.
*/
.header {
position: sticky;
top: 0;
z-index: 1;
padding-block: var(--size-px--m);
background-color: color-mix(in srgb, var(--header-ground) 40%, transparent);
backdrop-filter: blur(var(--blur));
}
.headerContent {
max-width: var(--page-width);
margin-inline: auto;
padding-inline: var(--size-px--xl);
}
.content {
width: 100%;
max-width: var(--page-width);
margin-inline: auto;
padding-block: var(--size-px--s) var(--size-px--l);
padding-inline: var(--size-px--xl);
}
/*
* The wordmark rides on `currentColor` through a CSS mask, so it follows the
* theme. Swap the masked SVG for your own logo.
*/
.logo {
width: 7rem;
max-width: 100%;
aspect-ratio: 300 / 57;
background: currentcolor;
mask: url("./mittwald.svg") no-repeat center / contain;
-webkit-mask: url("./mittwald.svg") no-repeat center / contain;
}
/* The global navigation is pushed to the far end of the top bar. */
.topnav {
margin-inline-start: auto;
}
/* The bell and the burger take that place once the navigation gives it up. */
.mobileActions {
margin-inline-start: auto;
}
/* Sidebar and content fill the space between top bar and footer. */
.body {
flex: 1;
min-height: 0;
}
/*
* The navigation card keeps a fixed width beside the flexible content. It stays
* below the header while the page scrolls, and scrolls its own links once they
* outgrow the viewport.
*/
.sidebar {
flex: 0 0 17rem;
position: sticky;
top: calc(var(--header-height) + var(--size-px--s));
align-self: flex-start;
max-height: calc(
100dvh - var(--header-height) - var(--size-px--s) - var(--size-px--l)
);
overflow: auto;
}
.main {
flex: 1 1 auto;
min-width: 0;
}
.footer {
margin-block-start: auto;
}
/*
* Below this width neither the global navigation nor the sidebar fits, so both
* hand their links to the off-canvas behind the burger.
*/
@media (max-width: 768px) {
.topnav,
.sidebar {
display: none;
}
.headerContent,
.content {
padding-inline: var(--size-px--m);
}
}
@media (min-width: 769px) {
.mobileActions {
display: none;
}
}
@media (prefers-contrast: more), (prefers-reduced-transparency) {
.header {
background-color: var(--header-ground);
backdrop-filter: none;
}
}
/* The dark gradient starts darker than the neutral token. */
[data-theme="dark"] .app {
--header-ground: #121518;
}
```
## Doku-Layout
Eine einfachere Abwandlung ohne globale Objekt-Tiefe: Die
[Navigation](https://flow.mittwald.de/components/navigation/navigation) listet die Bereiche flach, der
Content steht mittig, und rechts führt ein eigenes Inhaltsverzeichnis („Auf
dieser Seite") durch die Abschnitte der Seite. Dieser Styleguide selbst folgt
dem Muster.
**complex-app-doku.tsx**
```tsx
import {
ActionGroup,
Badge,
Button,
Content,
Flex,
HeaderNavigation,
Heading,
Icon,
IconMenu,
IconSearch,
Label,
LayoutCard,
Link,
Modal,
ModalTrigger,
Navigation,
NavigationGroup,
Section,
Text,
} from "@mittwald/flow-react-components";
import { IconContrastFilled } from "@tabler/icons-react";
import styles from "./complex-app-doku.module.css";
const lorem =
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.";
export default () => (
Button{lorem}Colors{lorem}Light und Dark{lorem}Sizes{lorem}Variants{lorem}Auf dieser Seite
Button
Colors
Light und Dark
Sizes
Variants
Datenschutz
Impressum
);
/*
* Search keeps its place next to the burger: it is worth reaching without
* opening the menu first.
*/
const SearchButton = () => (
);
/*
* The section links, shared by the top bar and the off-canvas. Each renders
* them through its own props context, so the same Links read as a header bar
* in one and as a navigation list in the other.
*/
const GlobalNavigationLinks = () => (
<>
Get Started
Foundations
Templates
Components
>
);
const ComponentNavigation = () => (
Action
ActionGroup
Button
ContextMenu
Autocomplete
Checkbox
CheckboxButton
CodeEditorNeu
Combobox
DatePicker
Select
Switch
);
/*
* On a narrow screen the top bar has no room for the sections and the
* component navigation has no column of its own, so both move in here.
*/
const MobileMenu = () => (
MenüComponents
)
```
**complex-app-doku.module.css**
```css
.app {
/* Header and content share one maximum width, so their edges line up. */
--page-width: 1800px;
/* The header's tint is mixed from the gradient's first stop. */
--header-ground: var(--neutral--color--200);
/* The header's rendered height; the sticky sidebar starts just below it. */
--header-height: 72px;
/* Container-relative here; use 100dvh in a real full-page app. */
min-height: 100%;
background: var(--texture--grain), var(--color--background-gradient);
background-blend-mode: overlay, normal;
}
/*
* The header stays at the top while the page scrolls beneath it. Its tint is
* translucent and blurred, so the navigation stays legible above the content.
*/
.header {
position: sticky;
top: 0;
z-index: 1;
padding-block: var(--size-px--m);
background-color: color-mix(in srgb, var(--header-ground) 40%, transparent);
backdrop-filter: blur(var(--blur));
}
.headerContent {
max-width: var(--page-width);
margin-inline: auto;
padding-inline: var(--size-px--xl);
}
.content {
width: 100%;
max-width: var(--page-width);
margin-inline: auto;
padding-block: var(--size-px--s) var(--size-px--l);
padding-inline: var(--size-px--xl);
}
/*
* The wordmark rides on `currentColor` through a CSS mask, so it follows the
* theme. Swap the masked SVG for your own logo.
*/
.logo {
width: 8rem;
max-width: 100%;
aspect-ratio: 300 / 57;
background: currentcolor;
mask: url("./mittwald.svg") no-repeat center / contain;
-webkit-mask: url("./mittwald.svg") no-repeat center / contain;
}
/* The global navigation is pushed to the far end of the top bar. */
.topnav {
margin-inline-start: auto;
}
/* Search and the burger take that place once the navigation gives it up. */
.mobileActions {
margin-inline-start: auto;
}
/* Navigation, content and table of contents sit in three columns. */
.body {
flex: 1;
min-height: 0;
}
/*
* Both cards keep a fixed width; only the content column flexes. They stay below
* the header while the page scrolls, and scroll their own links once they
* outgrow the viewport.
*/
.sidebar,
.toc {
flex: 0 0 17rem;
position: sticky;
top: calc(var(--header-height) + var(--size-px--s));
align-self: flex-start;
max-height: calc(
100dvh - var(--header-height) - var(--size-px--s) - var(--size-px--l)
);
overflow: auto;
}
.main {
flex: 1 1 20rem;
}
.footer {
margin-block-start: auto;
}
/*
* Below this width neither the section links nor the component navigation
* fits, so both hand their links to the off-canvas behind the burger.
*/
@media (max-width: 768px) {
.topnav,
.sidebar {
display: none;
}
.headerContent,
.content {
padding-inline: var(--size-px--m);
}
}
@media (min-width: 769px) {
.mobileActions {
display: none;
}
}
@media (prefers-contrast: more), (prefers-reduced-transparency) {
.header {
background-color: var(--header-ground);
backdrop-filter: none;
}
}
/* The dark gradient starts darker than the neutral token. */
[data-theme="dark"] .app {
--header-ground: #121518;
}
```
---
# Detailseite
```tsx
import {
Breadcrumb,
Button,
ColumnLayout,
Content,
ContextMenu,
ContextMenuTrigger,
Flex,
Header,
Heading,
IconChevronDown,
IconDelete,
IconEdit,
IconExternalLink,
IconPassword,
Label,
LabeledValue,
LayoutCard,
Link,
MenuItem,
Modal,
ModalTrigger,
ProgressBar,
Section,
Switch,
Tab,
Table,
TableBody,
TableCell,
TableColumn,
TableHeader,
TableRow,
TabNavigation,
Tabs,
TabTitle,
Text,
} from "@mittwald/flow-react-components";
export default () => {
return (
Projekt
E-Mails
E-Mail-Details
max@mustermann.de
Allgemein
Weiterleitungen
Autoresponder
E-Mail-AdressePasswort ändern...E-Mail-Adresse bearbeiten...max@mustermann.de
mittwald Webmailer
SpeicherplatzSpeicherplatz bearbeiten...
Die E-Mail-Adresse verfügt über einen
Speicherplatz zum Empfangen und Speichern von
E-Mails. Wir empfehlen dir, mindestens 2 GB zu
reservieren.
Verbindungsinformationen
{["IMAP", "POP3", "SMTP"].map(
(connectionType) => (
{connectionType}
Benutzername
max@mustermann.de
{connectionType} Port
...
{connectionType} Postausgang
...
),
)}
Spamschutz
Der Spamfilter schützt dich vor ungewollten
E-Mails. Niemand will Müll in seinem Postfach,
daher empfehlen wir den Spamschutz immer
aktiviert zu lassen.
);
}
```
---
# Verwendung
Verwende die Detailseite, wenn sich Nutzer in **ein einzelnes Objekt** vertiefen
und es bearbeiten sollen – sobald zu einem Datensatz mehr gehört als eine Zeile
in einer Liste. Geht es um viele gleichartige Objekte nebeneinander, führt die
[Übersichtsseite](https://flow.mittwald.de/templates/seiten/uebersichtsseite) dorthin.
---
# Aufbau
- **Content Header** – Eine [Heading](https://flow.mittwald.de/components/content/heading) als H1
benennt das Objekt und macht klar, in welchem Bereich sich der Nutzer
befindet. Eine [Breadcrumb](https://flow.mittwald.de/components/navigation/breadcrumb) ergänzt die
Orientierung, navigiert im Seitenbaum und führt schnell auf übergeordnete
Ebenen zurück; ihre Einträge bleiben möglichst generisch.
- **Aktionsmenü** – Ein [Button](https://flow.mittwald.de/components/actions/button) mit einem
[ContextMenu](https://flow.mittwald.de/components/actions/context-menu) bündelt die wichtigsten
Aktionen des Objekts und dient den Nutzern als Schnellzugriff.
- **Content** – Eine [LayoutCard](https://flow.mittwald.de/components/structure/layout-card) ist die
Grundlage jeder Detailseite. Sie bildet den äußeren Container für den Inhalt
und stellt Hintergrund und Padding bereit.
- **Themenblöcke** – [Sections](https://flow.mittwald.de/components/structure/section) gliedern den
Content in Themenblöcke mit eigener H2-Überschrift und eigenen Aktionen im
Header. Sie lassen sich beliebig verschachteln, um auch umfangreiche Inhalte
strukturiert darzustellen. Ordne sie nach Wichtigkeit und staffle Details nach
unten.
- **Tab-Unterseiten** (optional) – Bei umfangreichen Objekten verteilt eine
[TabNavigation](https://flow.mittwald.de/components/navigation/tab-navigation) die Themenblöcke auf
gleichrangige Unterseiten. Nutzer wechseln zwischen ihnen, ohne den Kontext
des Objekts zu verlieren.
---
# Dashboard
```tsx
import {
AlertBadge,
Avatar,
Button,
ColumnLayout,
Content,
Header,
Heading,
IconCheck,
IconEmail,
Image,
LayoutCard,
Link,
Section,
Text,
typedList,
} from "@mittwald/flow-react-components";
export default () => {
const List = typedList<{
title: string;
date: string;
status?: "info" | "warning" | "danger";
isInvite?: boolean;
}>();
return (
Hey Max!
Benachrichtigungen alert("not implemented")}
>
i.title}>
{(i) => (
{i.isInvite ? (
) : (
)}
{i.title}{i.date}
{i.isInvite ? (
) : (
)}
)}
mStudio entdecken
Du willst das Beste aus deinen Projekten
herausholen? Unsere{" "}
Wissensdatenbank hilft
dir dabei, das mStudio und seine
Möglichkeiten besser kennenzulernen.
Status
Alle Systeme funktionieren
Das mStudio und alle dazugehörenden Systeme
laufen einwandfrei.
Zur Statusseite
Neues aus dem Blog
Zukunft pflanzen – mittwald unterstützt 8000
m² Aufforstung für den Klimaschutz
Nachhaltigkeitsmanager Marvin von mittwald
berichtet über ein regionales
Aufforstungsprojekt mit 8.000 m² neuem Wald.
Zum Blogartikel
);
}
```
---
# Verwendung
Verwende ein Dashboard, wenn Nutzer **mehrere Themen gleichzeitig** im Blick
behalten und die wichtigsten davon direkt erreichen sollen. Zeige nur, was einen
Mehrwert bietet, und ordne die Kacheln nach Relevanz. Geht es um viele
gleichartige Objekte in einer durchsuchbaren Liste, führt die
[Übersichtsseite](https://flow.mittwald.de/templates/seiten/uebersichtsseite) dorthin.
---
# Aufbau
- **Content Header** – Eine [Heading](https://flow.mittwald.de/components/content/heading) als H1
benennt den Bereich, dessen Lage das Dashboard zusammenfasst.
- **Kachel-Anordnung** – Ein [ColumnLayout](https://flow.mittwald.de/components/structure/column-layout)
verteilt die Kacheln über die verfügbaren Spalten und passt sich an
unterschiedliche Breakpoints an.
- **Kacheln** – Jede Kachel ist eine
[LayoutCard](https://flow.mittwald.de/components/structure/layout-card), in der
[Sections](https://flow.mittwald.de/components/structure/section) den Inhalt in Header, Content und
Footer gliedern. Jede Kachel bildet genau ein Thema ab.
- **Visuelle Aufbereitung** – Kennzahlen und Trends werden mit Komponenten wie
[ProgressBar](https://flow.mittwald.de/components/status/progress-bar),
[DonutChart](https://flow.mittwald.de/components/data-visualisation/donut-chart) oder einem
[Image](https://flow.mittwald.de/components/content/image) sofort ablesbar; eine
[AccentBox](https://flow.mittwald.de/components/structure/accent-box) hebt einzelne Kacheln gezielt
hervor.
---
# Varianten und Abwandlung
## Detail-Dashboard
Ein Detail-Dashboard gibt einen schnellen Einblick in **ein einzelnes Objekt**:
Status, relevante Kennzahlen und Zusammenhänge, oft durch Diagramme
visualisiert. Dafür eignen sich [ProgressBar](https://flow.mittwald.de/components/status/progress-bar),
[DonutChart](https://flow.mittwald.de/components/data-visualisation/donut-chart),
[BigNumber](https://flow.mittwald.de/components/data-visualisation/big-number) und
[CartesianChart](https://flow.mittwald.de/components/data-visualisation/cartesian-chart).
```tsx
import {
Breadcrumb,
ColumnLayout,
DonutChart,
Flex,
Header,
Heading,
Label,
LabeledValue,
LayoutCard,
Link,
ProgressBar,
Section,
Text,
} from "@mittwald/flow-react-components";
export default () => {
return (
Projekt
Dashboard
Dashboard
Projektdetailsp12345
Mein Server
vCPU Auslastung
Details
43 %05.09.2025, 07:30 Uhr67 %RAM Auslastung
Details
12 %05.09.2025, 07:30 Uhr24 %Speicherplatz
Details
);
}
```
## Kachel mit AccentBox
Eine [AccentBox](https://flow.mittwald.de/components/structure/accent-box) setzt gezielt einen
farblichen Akzent, etwa für einen Hinweis oder eine Werbemaßnahme. Setze sie
sparsam ein – in der Regel genügt eine farbige Kachel, damit das Gesamtbild
ruhig bleibt.
```tsx
import {
AccentBox,
Breadcrumb,
Button,
ColumnLayout,
Flex,
Header,
Heading,
Icon,
LayoutCard,
Link,
Section,
Text,
} from "@mittwald/flow-react-components";
import { IconHeartHandshake } from "@tabler/icons-react";
export default () => {
return (
Organisation
Dashboard
Dashboard
Vertragspartner
Mittwald CM Service GmbH & Co.KG
Max Mustermann
Königsberger Straße 4-6, 32339 Espelkamp
mittwald@mittwald.de
+49 1234 5678910
RechnungsempfängerRechnung
Max Mustermann
Königsberger Straße 4-6, 32339 Espelkamp
mittwald@mittwald.de
+49 1234 5678910
Entwickle deine eigene Extension
Du hast Lust als Organisation deine eigene
Extension zu bauen und sie der Community
bereitzustellen? Du hast vorab noch Fragen
rund um die Contribution? Lass uns drüber
quatschen.
Contributor werden
);
}
```
---
# Übersichtsseite
```tsx
import {
ActionGroup,
Avatar,
Button,
Content,
ContextMenu,
ContextMenuTrigger,
Flex,
Heading,
IconChevronDown,
IconDomain,
IconHome,
IconInfo,
IconMove,
IconSubdomain,
IconVhost,
LayoutCard,
ListItemView,
MenuItem,
Text,
typedList,
} from "@mittwald/flow-react-components";
export default () => {
const DomainList = typedList<{
id: string;
domain: string;
type: string;
target?: string;
}>();
return (
Domains
domain.id}
>
domain.domain}
>
{(domain) => (
{domain.type === "Projekt-Domain" ? (
) : domain.type === "Subdomain" ? (
) : domain.type === "Domain" ? (
) : (
)}
{domain.domain}{domain.type}
{domain.target
? domain.target
: "Kein Ziel"}
)}
);
}
```
---
# Verwendung
Verwende die Übersichtsseite, wenn Nutzer aus **vielen gleichartigen Objekten**
das passende auswählen und anschließend im Detail bearbeiten sollen. Die Tiefe
eines einzelnen Objekts gehört auf seine
[Detailseite](https://flow.mittwald.de/templates/seiten/detailseite); ist die Liste nur ein Abschnitt
neben anderen Inhalten, gehört sie in ein
[Dashboard](https://flow.mittwald.de/templates/seiten/dashboard).
---
# Aufbau
- **Content Header** – Eine [Heading](https://flow.mittwald.de/components/content/heading) als H1
benennt den Objekttyp, den die Seite auflistet.
- **Content** – Eine [LayoutCard](https://flow.mittwald.de/components/structure/layout-card) umschließt
die [List](https://flow.mittwald.de/components/list/list), die das Hauptelement der Seite ist.
- **List** – Die [List](https://flow.mittwald.de/components/list/list) bringt Suche, Filter und
Sortierung bereits mit. Je nach Inhalt lässt sich ihre Ansicht als Liste,
Raster oder Tabelle wählen.
- **Eintrag** – Ein [List-Item](https://flow.mittwald.de/components/list/list-item-view) je Objekt
verlinkt in seine [Detailseite](https://flow.mittwald.de/templates/seiten/detailseite) und trägt
häufige Aktionen direkt am Objekt, etwa über ein
[ContextMenu](https://flow.mittwald.de/components/actions/context-menu).
- **Anlege-Aktion** – Sie liegt in der
[ActionGroup](https://flow.mittwald.de/components/actions/action-group) der List und bleibt so am
Objekttyp, auch wenn die Liste später an anderer Stelle auftaucht.
- **Leerzustand** – Die List bringt automatisch eine
[Empty View](https://flow.mittwald.de/components/list/list#lade-und-leeransichten-empty-view) mit;
passe sie an das anzulegende Objekt an, denn der erste Kontakt mit einem
Objekttyp ist fast immer die leere Liste.
---
# Varianten und Abwandlung
## Als Kachelansicht
In der Kachelansicht steht ein großes Vorschaubild im Vordergrund. So erkennen
Nutzer das passende Objekt auf einen Blick. Struktur und Ausstattung der Seite
bleiben gleich. Es ändern sich nur `defaultViewMode` und die Anzeigeform des
Eintrags.
```tsx
import {
ActionGroup,
Avatar,
Button,
ContextMenu,
Flex,
Heading,
IconApp,
LayoutCard,
MenuItem,
Text,
typedList,
} from "@mittwald/flow-react-components";
export default () => {
const AppList = typedList<{
id: string;
name: string;
type: string;
domain: string;
}>();
return (
Apps
app.id}
defaultViewMode="tiles"
>
"#"}
textValue={(app) => app.name}
>
{(app) => (
{app.name}{app.type}
)}
);
}
```
## Tabs über mehreren Übersichtsseiten
Manchmal gehören mehrere Objekttypen fachlich zusammen, etwa E-Mail-Adressen,
Weiterleitungen und Delivery-Boxen. Eine
[TabNavigation](https://flow.mittwald.de/components/navigation/tab-navigation) fasst sie zusammen. Jeder
Tab ist für sich eine Übersichtsseite.
---
# Anlegen & Bearbeiten
```tsx
import {
Action,
ActionGroup,
Button,
CheckboxButton,
ColumnLayout,
Content,
FieldDescription,
Heading,
Label,
Modal,
NumberField,
Option,
PasswordCreationField,
Section,
Select,
TextField,
useModalController,
} from "@mittwald/flow-react-components";
import {
Form,
SubmitButton,
typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm } from "react-hook-form";
export default () => {
const controller = useModalController();
const form = useForm<{
localPart: string;
domain: string;
password: string;
storageInGb: number;
spamProtection: boolean;
}>({
defaultValues: {
localPart: "",
domain: "mustermann.de",
password: "",
storageInGb: 2,
spamProtection: true,
},
});
const Field = typedField(form);
const handleSubmit = async () => {
/** ... anlegen */
return () => {
// Erst nach erfolgreichem Anlegen schließen
controller.close();
};
};
return (
<>
>
);
}
```
---
# Verwendung
Verwende dieses Overlay für Eingaben, die zu einem Objekt gehören: anlegen,
bearbeiten oder konfigurieren. Wird nichts eingegeben, sondern nur eine
Entscheidung getroffen, nimm ein Overlay zum
[Bestätigen](https://flow.mittwald.de/templates/overlays/bestaetigen); hängt die Eingabe von einer
vorgelagerten Auswahl ab, eines mit
[mehreren Schritten](https://flow.mittwald.de/templates/overlays/mehrere-schritte).
---
# Aufbau
- **Overlay-Container** – Ein [Modal](https://flow.mittwald.de/components/overlays/modal) legt sich über
die auslösende Seite; nach dem Speichern kehrt der Nutzer an dieselbe Stelle
zurück. Je nach Umfang des Formulars wird das mittige Modal gewählt oder – für
lange, ausführliche Inhalte, die sonst im Modal scrollen müssten – die
`offCanvas`-Variante.
- **Überschrift** – Eine [Heading](https://flow.mittwald.de/components/content/heading) benennt Aktion
und Objekt, etwa „E-Mail-Adresse anlegen". Sie ist meist der einzige Hinweis
auf den Bezug des Formulars, da die auslösende Seite verdeckt ist.
- **Formular** – Ein [Form](https://flow.mittwald.de/components/react-hook-form/form) fasst die Felder
zusammen. Umfangreiche Formulare lassen sich mit
[Sections](https://flow.mittwald.de/components/structure/section) in Abschnitte unterteilen.
- **Aktionen** – Das Formular wird über den
[SubmitButton](https://flow.mittwald.de/components/react-hook-form/submit-button) in der
[ActionGroup](https://flow.mittwald.de/components/actions/action-group) abgesendet. Sie enthält
außerdem immer einen „Abbrechen"-Button, der das Overlay wieder schließt.
---
# Varianten und Abwandlung
## Bearbeiten
Anlegen und Bearbeiten teilen sich ein Template. Sie unterscheiden sich in
Überschrift, Button-Beschriftung und der Vorbelegung der Felder.
```tsx
import {
Action,
ActionGroup,
Button,
CheckboxButton,
ColumnLayout,
Content,
Heading,
Label,
Modal,
NumberField,
Section,
TextField,
useModalController,
} from "@mittwald/flow-react-components";
import {
Form,
SubmitButton,
typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm } from "react-hook-form";
export default () => {
const controller = useModalController();
const form = useForm<{
description: string;
storageInGb: number;
spamProtection: boolean;
}>({
defaultValues: {
description: "Persönliches Postfach",
storageInGb: 2,
spamProtection: true,
},
});
const Field = typedField(form);
const handleSubmit = async () => () => controller.close();
return (
<>
>
);
}
```
## Kurzes Formular
Bei wenigen Feldern ist das Formular im mittigen
[Modal](https://flow.mittwald.de/components/overlays/modal) gut aufgehoben. Hier kann der Fokus beim
Öffnen direkt in das erste [TextField](https://flow.mittwald.de/components/form-controls/text-field)
springen (`autoFocus`), sodass die Eingabe ohne weiteren Klick beginnt. Setze
`autoFocus` nur, wenn das Overlay ausschließlich diesem Eingabezweck dient –
sonst überspringen Screenreader-Nutzer den einleitenden Kontext aus Überschrift
und Beschreibung.
```tsx
import {
Action,
ActionGroup,
Button,
Content,
Heading,
Label,
Modal,
Section,
TextField,
useModalController,
} from "@mittwald/flow-react-components";
import {
Form,
SubmitButton,
typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm } from "react-hook-form";
export default () => {
const controller = useModalController();
const form = useForm<{ description: string }>({
defaultValues: { description: "Persönliches Postfach" },
});
const Field = typedField(form);
const handleSubmit = async () => () => controller.close();
return (
<>
>
);
}
```
## Fehlende Voraussetzung
Manchmal hängt das Anlegen an einer Voraussetzung, die noch fehlt – etwa ein
Vertragspartner, ohne den eine Extension nicht installiert werden kann. Ein
[Alert](https://flow.mittwald.de/components/status/alert) im Overlay erklärt die fehlende Voraussetzung
und bietet an, den nötigen Teilprozess direkt hier zu starten. So bleibt der
Nutzer im Kontext und kehrt nach dem Anlegen an dieselbe Stelle zurück. Die
abschließende Aktion bleibt deaktiviert, bis die Voraussetzung erfüllt ist.
```tsx
import {
Action,
ActionGroup,
Alert,
Button,
Content,
Heading,
Label,
Modal,
Option,
Section,
Select,
Text,
useModalController,
} from "@mittwald/flow-react-components";
export default () => {
const controller = useModalController();
return (
<>
Extension installieren
Wähle die Organisation, in der die Extension
bereitgestellt werden soll.
Kein Vertragspartner hinterlegt
Für diese Organisation ist noch kein
Vertragspartner hinterlegt. Lege einen an,
um mit der Installation fortzufahren.
>
);
}
```
---
# Bestätigen
```tsx
import {
Action,
ActionGroup,
Button,
Content,
Heading,
Modal,
ModalTrigger,
Text,
} from "@mittwald/flow-react-components";
export default () => (
E-Mail-Adresse löschen
Die E-Mail-Adresse max@mustermann.de und
alle darin gespeicherten E-Mails werden gelöscht.
Das lässt sich nicht rückgängig machen. Die
Weiterleitung von kontakt@mustermann.de auf
diese Adresse geht danach ins Leere.
)
```
---
# Verwendung
Verwende dieses Overlay, wenn eine Aktion Daten unwiederbringlich entfernt,
etwas Kostenpflichtiges beendet oder weitere Objekte betrifft – etwa löschen,
kündigen oder eine Verknüpfung auflösen. Der Sinn dieses Overlays ist, den
Workflow bewusst zu unterbrechen und den Nutzer auf die Tragweite der Aktion
aufmerksam zu machen. Lässt sich eine Aktion dagegen sofort und mit geringem
Aufwand rückgängig machen, wirkt eine Rückfrage schnell lästig und das Overlay
ist verzichtbar.
---
# Aufbau
- **Overlay-Container** – Ein [Modal](https://flow.mittwald.de/components/overlays/modal) erscheint
mittig und ist so groß wie sein Inhalt. Meist ist das Modal die richtige Wahl,
da es um eine kurze Rückfrage geht; werden viele abhängige Objekte
aufgelistet, die mitgelöscht werden, kann die `offCanvas`-Variante sinnvoll
sein.
- **Überschrift und Folgen** – Die Überschrift ist kurz und prägnant. Ein
[Text](https://flow.mittwald.de/components/content/text) darunter klärt den Nutzer verständlich über
die Folgen der Aktion auf. Wichtige Informationen (z. B. das zu löschende
Objekt) können mit **Fettschrift** hervorgehoben werden. Betrifft die Aktion
weitere Objekte, werden sie hier genannt.
- **Aktionen** – Eine [ActionGroup](https://flow.mittwald.de/components/actions/action-group) trägt die
bestätigende Aktion mit der Aktion als Verb, daneben ein zurückhaltendes
„Abbrechen". Je nach Konsequenz nutzt die bestätigende Aktion einen
[Button](https://flow.mittwald.de/components/actions/button) in `color="success"` oder
`color="danger"`, um die Folge zu verdeutlichen.
---
# Varianten und Abwandlung
## Mit Eingabe der Bestätigung
Bei besonders folgenreichen Aktionen, etwa dem Löschen des eigenen Profils,
tippt der Nutzer den Namen des Objekts ab, bevor der Button aktiv wird.
```tsx
import {
Action,
ActionGroup,
Button,
Content,
FieldDescription,
Heading,
Label,
Modal,
ModalTrigger,
Section,
Text,
TextField,
} from "@mittwald/flow-react-components";
import { useState } from "react";
export default () => {
const profileName = "Max Mustermann";
const [confirmation, setConfirmation] = useState("");
return (
Profil löschen
Das Profil {profileName} wird mit allen
zugehörigen Daten und Zugriffsrechten
gelöscht. Das lässt sich nicht rückgängig
machen.
Tippe {profileName} ab, um das
Löschen zu bestätigen.
);
}
```
---
# Anzeigen
```tsx
import {
Action,
ActionGroup,
Button,
CodeBlock,
Content,
Heading,
Modal,
ModalTrigger,
} from "@mittwald/flow-react-components";
export default () => {
const log = `[2026-08-10 03:00:01] INFO cron: starting job "backup-database"
[2026-08-10 03:00:01] INFO backup: connecting to database "app_production"
[2026-08-10 03:00:02] INFO backup: dumping database "app_production"
[2026-08-10 03:00:09] INFO backup: 42 tables dumped, 1.284.902 rows total
[2026-08-10 03:00:14] INFO backup: 248.6 MB written to /backups/2026-08-10.sql.gz
[2026-08-10 03:00:14] INFO backup: verifying checksum sha256:9f2b…c41e
[2026-08-10 03:00:15] INFO backup: upload to offsite storage "eu-central" started
[2026-08-10 03:00:22] INFO backup: upload finished in 7.4s
[2026-08-10 03:00:22] WARN backup: retention exceeded, removing /backups/2026-07-27.sql.gz
[2026-08-10 03:00:23] INFO backup: 30 snapshots retained, oldest 2026-07-11
[2026-08-10 03:00:23] INFO cron: job "backup-database" finished with exit code 0`;
return (
Cronjob „backup-database" – Durchlauf vom
10.08.2026
);
}
```
---
# Verwendung
Verwende dieses Overlay für Inhalte, die zu einem Objekt gehören, die Seite aber
sprengen würden und aus denen keine weitere Aktion folgt: Details, Protokolle,
Vorschauen oder Belege. Wird etwas eingegeben, nimm ein Overlay zum
[Anlegen & Bearbeiten](https://flow.mittwald.de/templates/overlays/anlegen-bearbeiten); ist eine
Entscheidung zu treffen, eines zum
[Bestätigen](https://flow.mittwald.de/templates/overlays/bestaetigen). Was jeder Besucher dauerhaft
sehen soll, gehört fest auf die [Detailseite](https://flow.mittwald.de/templates/seiten/detailseite).
---
# Aufbau
- **Overlay-Container** – Bei wenig Inhalt eignet sich ein
[Modal](https://flow.mittwald.de/components/overlays/modal), das mittig erscheint; bei viel Inhalt mit
erwartetem Scrollen – etwa ein Log oder eine lange Liste – bietet sich die
`offCanvas`-Variante an, die für sich scrollt.
- **Überschrift** – Eine [Heading](https://flow.mittwald.de/components/content/heading) benennt kurz das
gezeigte Objekt.
- **Content** – Der Inhalt lässt sich mit
[Sections](https://flow.mittwald.de/components/structure/section) und einem
[ColumnLayout](https://flow.mittwald.de/components/structure/column-layout) übersichtlich
strukturieren. Werte, die weiterverwendet werden, erhalten einen
[CopyButton](https://flow.mittwald.de/components/actions/copy-button); das Overlay ist oft der einzige
Ort, an dem ein Wert vollständig steht.
- **Aktion** – Eine [ActionGroup](https://flow.mittwald.de/components/actions/action-group) trägt als
einzige Aktion „Schließen".
---
# Varianten und Abwandlung
## Einmal-Anzeige eines Geheimnisses
Ein Sonderfall, in dem das Overlay nach dem Anlegen erscheint: Das Ergebnis
enthält einen Wert, der später nicht erneut abrufbar ist – ein API-Token,
Recovery-Codes oder ein Auth-Code. Der Warnhinweis steht vor dem Wert, damit vor
dem Schließen klar ist, dass es keine zweite Gelegenheit gibt.
```tsx
import {
Action,
ActionGroup,
Button,
Content,
CopyButton,
Heading,
InlineCode,
Label,
LabeledValue,
Modal,
ModalTrigger,
Section,
Text,
} from "@mittwald/flow-react-components";
export default () => (
API-Token kopieren
Dein persönlicher Token wird dir{" "}
nur einmalig angezeigt. Speichere ihn an
einem sicheren Ort ab.
0664f2ab-9556-4a96-a95c-ea72800f379c
)
```
## IllustratedMessage
Eine [IllustratedMessage](https://flow.mittwald.de/components/content/illustrated-message) wird
verwendet, wenn eine Botschaft im Modal besonders hervorgehoben werden soll. Sie
eignet sich vor allem für wichtige Zustände wie
[Fehler](https://flow.mittwald.de/foundations/content-guidelines/fehlermeldungen#fehlermeldungen-anzeigen-bei-fehlgeschlagenen-aktionen),
leere Inhalte oder abgeschlossene Aktionen und macht diese visuell schnell
erfassbar.
```tsx
import {
Action,
ActionGroup,
Button,
Content,
Heading,
IconDanger,
IllustratedMessage,
Modal,
ModalTrigger,
Text,
} from "@mittwald/flow-react-components";
export default () => (
Löschen fehlgeschlagen
Die E-Mail-Adresse konnte nicht gelöscht werden.
Wir arbeiten daran, das Problem zu beheben.
Bitte versuche es später noch einmal.
)
```
---
# Mehrere Schritte
```tsx
import {
Action,
ActionGroup,
Alert,
Button,
Content,
FieldDescription,
Heading,
Label,
Modal,
NumberField,
Option,
RadioButton,
RadioGroup,
Section,
Select,
Text,
TextArea,
TextField,
useModalController,
} from "@mittwald/flow-react-components";
import { useState } from "react";
type RecordType =
"a" | "cname" | "mx" | "txt" | "caa" | "srv";
type Step = "selectType" | "configure";
export default () => {
const controller = useModalController();
const [step, setStep] = useState("selectType");
const [type, setType] = useState("a");
const recordTypes: {
id: RecordType;
name: string;
description: string;
}[] = [
{
id: "a",
name: "A / AAAA",
description:
"Verweist den Namen auf eine IPv4- oder IPv6-Adresse.",
},
{
id: "cname",
name: "CNAME",
description:
"Verweist den Namen als Alias auf einen anderen Hostnamen.",
},
{
id: "mx",
name: "MX",
description:
"Legt fest, welcher Mailserver E-Mails für die Domain annimmt.",
},
{
id: "txt",
name: "TXT",
description:
"Hinterlegt einen frei wählbaren Text, etwa zur Verifizierung oder für SPF.",
},
{
id: "caa",
name: "CAA",
description:
"Bestimmt, welche Zertifizierungsstelle Zertifikate ausstellen darf.",
},
{
id: "srv",
name: "SRV",
description:
"Gibt an, unter welchem Host und Port ein Dienst erreichbar ist.",
},
];
// The heading takes up the selection: "DNS-Eintrag anlegen" becomes
// e.g. "MX-Eintrag anlegen".
const headlines: Record = {
a: "A-/AAAA-Eintrag anlegen",
cname: "CNAME-Eintrag anlegen",
mx: "MX-Eintrag anlegen",
txt: "TXT-Eintrag anlegen",
caa: "CAA-Eintrag anlegen",
srv: "SRV-Eintrag anlegen",
};
// Every record type carries a time-to-live.
const ttlField = () => (
);
// The selected type decides which fields follow — that is why this is a
// separate step and not a field in one form.
const configFields = (recordType: RecordType) => {
switch (recordType) {
case "a":
return (
<>
Mit einem A-Record (A steht für Address)
leitest du eine Domain oder Subdomain an eine
IP-Adresse weiter. Er ist also entscheidend
für die Zuordnung von Domainnamen zu
physischen Servern im Internet.
{ttlField()}
>
);
case "mx":
return (
<>
Ein MX-Record leitet E-Mails an einen
Mailserver weiter. Es ist üblich, für eine
Domäne mehrere MX-Records zu definieren mit
unterschiedlichen Prioritäten, so dass bei
Ausfall eines Mail-Servers ein anderer die
E-Mails entgegennehmen kann.
{ttlField()}
>
);
case "txt":
return (
<>
Ein TXT-Record ist ein Eintrag im DNS, mit dem
ein frei definierbarer Text in einer DNS-Zone
abgelegt werden kann.
{ttlField()}
>
);
case "caa":
return (
<>
Mit einem CAA-Record (Certificate Authority
Authorization) können Domain-Inhaber über das
DNS festlegen, welche Zertifizierungsstellen
berechtigt sind, Zertifikate für ihre Domain
oder Subdomain auszustellen.
{ttlField()}
>
);
case "cname":
return (
<>
Ein Canonical Name- oder CNAME-Record ist ein
DNS-Eintrag, der einen Aliasnamen dem
eigentlichen oder kanonischen Domainnamen
zuordnet.
{ttlField()}
DNS-Records werden überschrieben
Wenn ein CNAME-Record für die DNS-Zone
angelegt wird, werden alle bereits
angelegten DNS-Records entfernt.
>
);
case "srv":
return (
<>
Mit einem SRV-Record kannst du einen Host und
einen Port für spezifische Dienste angeben.
{ttlField()}
z. B. _sip
>
);
}
};
return (
<>
{step === "selectType"
? "DNS-Eintrag anlegen"
: headlines[type]}
{step === "selectType" && (
Wähle aus, welche Art von Eintrag du anlegen
möchtest.
setType(value as RecordType)
}
>
{recordTypes.map((record) => (
{record.name}{record.description}
))}
)}
{step === "configure" && (
{configFields(type)}
)}
{step === "selectType" && (
)}
{step === "configure" && (
<>
>
)}
>
);
}
```
---
# Verwendung
Verwende mehrere Schritte, wenn eine Auswahl bestimmt, welche Eingaben danach
folgen, und diese Auswahl nicht nebenbei in einem Feld getroffen werden kann –
etwa weil Bild, Beschreibung oder Vergleich dazugehören. Stehen die Felder von
Anfang an fest, genügt ein Overlay zum
[Anlegen & Bearbeiten](https://flow.mittwald.de/templates/overlays/anlegen-bearbeiten).
---
# Aufbau
- **Overlay-Container** – Ein [Modal](https://flow.mittwald.de/components/overlays/modal), meist als
`offCanvas`. Die Schritte sind Zustände darin, keine eigenen Seiten.
- **Auswahl am Anfang** – Der erste Schritt trifft die Auswahl, von der alle
weiteren abhängen; eine Auswahl mitten im Vorgang würde vorherige Eingaben
entwerten. Jeder Schritt bildet eine Entscheidung ab, nicht nur eine
Feldergruppe.
- **Überschrift** – Eine [Heading](https://flow.mittwald.de/components/content/heading) wechselt mit dem
Schritt und nimmt die Auswahl auf – aus „DNS-Eintrag anlegen" wird „MX-Eintrag
anlegen".
- **Aktionen** – Eine [ActionGroup](https://flow.mittwald.de/components/actions/action-group) ordnet die
Buttons nach Farbe: die weiterführende Aktion rechts, „Abbrechen" links. Der
Zurück-Button führt zum vorherigen Schritt und erhält bei drei Buttons
`slot="secondary"`, damit er nicht mit „Abbrechen" zusammenfällt. Bereits
getätigte Eingaben bleiben beim Zurückgehen erhalten, soweit sie zur
geänderten Auswahl noch passen.
---
# Varianten und Abwandlung
## Weitere Abwandlungen
**Ein Overlay öffnet ein zweites.** Muss während des Vorgangs ein noch nicht
vorhandenes Objekt angelegt werden – eine Subdomain, ein API-Key – geschieht das
in einem eigenen Overlay mit eigenem Controller, nicht als weiterer Schritt. Das
erste Overlay bleibt darunter geöffnet und behält, was bereits eingegeben wurde.
```tsx
import {
Action,
ActionGroup,
Button,
ColumnLayout,
Content,
FieldDescription,
Header,
Heading,
InlineCode,
Label,
Modal,
Option,
Section,
Select,
Text,
TextField,
useModalController,
} from "@mittwald/flow-react-components";
import { useState } from "react";
export default () => {
const appController = useModalController();
const subdomainController = useModalController();
const [domains, setDomains] = useState([
"mustermann.de",
"muster-shop.de",
]);
const [domain, setDomain] = useState(null);
const [subdomain, setSubdomain] = useState("");
const addSubdomain = () => {
const name = `${subdomain}.mustermann.de`;
setDomains((all) => [...all, name]);
setDomain(name);
setSubdomain("");
subdomainController.close();
};
return (
<>
App anlegen
Deine App wird unter{" "}
{"/html/"}
{" "}
installiert
Hauptdomain zuweisen
{/*
* Its own controller, not another step: the app overlay stays
* open underneath and keeps what was already entered.
*/}
Subdomain anlegen
Eine Subdomain ist der Teil einer Domain, der
vor dem eigentlichen Domainnamen steht und
durch einen Punkt getrennt ist. Subdomains
sind eine praktische Möglichkeit, verschiedene
Bereiche einer Website voneinander zu trennen.
>
);
}
```
---
# Formular
```tsx
import {
ActionGroup,
ColumnLayout,
Heading,
Label,
Section,
TextField,
} from "@mittwald/flow-react-components";
import {
Form,
SubmitButton,
typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm } from "react-hook-form";
export default () => {
const form = useForm<{
firstName: string;
lastName: string;
street: string;
houseNumber: string;
zip: string;
city: string;
email: string;
phone?: string;
}>({
defaultValues: {
firstName: "",
lastName: "",
street: "",
houseNumber: "",
zip: "",
city: "",
email: "",
phone: "",
},
});
const Field = typedField(form);
return (
);
}
```
---
# Verwendung
Verwende diesen Baustein, wenn Nutzer zusammengehörige Daten eingeben,
bearbeiten oder übermitteln. Im mStudio steht ein Formular meist in einem
Overlay zum [Anlegen & Bearbeiten](https://flow.mittwald.de/templates/overlays/anlegen-bearbeiten).
---
# Aufbau
- **Form** – Ein [Form](https://flow.mittwald.de/components/react-hook-form/form) klammert die Felder
und verbindet sie über [Field](https://flow.mittwald.de/components/react-hook-form/field) mit React
Hook Form. Es sammelt die Werte und übergibt sie beim Absenden.
- **Struktur** – [Sections](https://flow.mittwald.de/components/structure/section) gruppieren
zusammengehörige Felder, ein
[ColumnLayout](https://flow.mittwald.de/components/structure/column-layout) ordnet zusammengehörige
Felder in eine Zeile – etwa Straße und Hausnummer oder PLZ und Ort. Ordne die
Felder nach Wichtigkeit und stelle Zusammengehöriges nah beieinander.
- **Absenden** – Eine [ActionGroup](https://flow.mittwald.de/components/actions/action-group) trägt den
[SubmitButton](https://flow.mittwald.de/components/react-hook-form/submit-button). Die Validierung
erfolgt standardmäßig erst beim Absenden. Quick Submit sendet das Formular
zusätzlich per ⌘/Strg + Enter aus jedem Feld ab – besonders hilfreich in einer
[TextArea](https://flow.mittwald.de/components/form-controls/text-area) oder einem
[MarkdownEditor](https://flow.mittwald.de/components/form-controls/markdown-editor).
### Field
Diese Punkte kehren über alle Form Controls hinweg wieder:
- **Label** – Ein gutes [Label](https://flow.mittwald.de/components/content/label) vermittelt alle
notwendigen Informationen klar und prägnant (max. 2 Wörter). Felder sind
standardmäßig Pflichtfelder; optionale Felder tragen den Zusatz „(optional)"
am Label. Lässt sich die Anforderung aus dem Kontext erschließen, kann ein
sichtbares Label entfallen – dann muss das Form Control über `aria-labelledby`
verknüpft oder mit `aria-label` beschrieben sein.
- **Fields & Controls** – Das Design System bietet viele wiederverwendbare Form
Controls: Fields wie [TextField](https://flow.mittwald.de/components/form-controls/text-field),
[NumberField](https://flow.mittwald.de/components/form-controls/number-field) oder
[PasswordCreationField](https://flow.mittwald.de/components/form-controls/password-creation-field)
sowie Controls wie [Checkbox](https://flow.mittwald.de/components/form-controls/checkbox),
[Select](https://flow.mittwald.de/components/form-controls/select) oder
[RadioGroup](https://flow.mittwald.de/components/form-controls/radio-group). Alle findest du unter
Components → Form Controls.
- **FieldDescription** – Die `` ist ein optionaler Hilfstext
unterhalb des Form Controls. Verwende sie sparsam: nur, wenn das Label allein
eine Frage offenlässt – etwa ein Beispiel, ein Format, eine Einheit, eine
Einschränkung oder eine Konsequenz.
- **FieldError** – Ein ungültiges Field zeigt immer eine Fehlermeldung über die
``. Sie erklärt verständlich, warum die Eingabe ungültig ist,
und hilft bei der Korrektur. Formulierungshinweise stehen in der Guideline zu
[Fehlermeldungen](https://flow.mittwald.de/foundations/content-guidelines/fehlermeldungen).
- **Placeholder** – Ein Placeholder ersetzt kein Label: Er verschwindet bei der
Eingabe und wird von Assistenztechnologien oft nicht zuverlässig erkannt.
Nutze ein `Label` für essenzielle Anforderungen und eine `FieldDescription`
für Beispiele oder Formatierungshinweise. Eine Ausnahme ist das
[SearchField](https://flow.mittwald.de/components/form-controls/search-field) mit seinem
kennzeichnenden Leading-Icon.
---
# Varianten und Abwandlung
## Mit Validierung
Nutze für die Validierung in der Regel
[React Hook Form](https://flow.mittwald.de/components/react-hook-form/form). Alternativ stehen
`isRequired` (für Pflichtfelder) und `validate` (für eigene Validierungen) zur
Verfügung. Bei ungültiger Eingabe wird das Field invalidiert; über die
`FieldError` kann eine Fehlermeldung ausgegeben werden.
```tsx
import {
ActionGroup,
Heading,
Label,
Section,
TextArea,
} from "@mittwald/flow-react-components";
import {
Form,
SubmitButton,
typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm } from "react-hook-form";
export default () => {
const form = useForm<{
publicKey: string;
}>({
defaultValues: {
publicKey: "",
},
});
const Field = typedField(form);
return (
);
}
```
## Progressive Disclosure
Felder erscheinen erst, wenn sie gebraucht werden. Ein übergeordnetes Control
wie eine [RadioGroup](https://flow.mittwald.de/components/form-controls/radio-group) oder eine
[Checkbox](https://flow.mittwald.de/components/form-controls/checkbox) blendet die abhängigen Felder ein
und hält das Formular so schlank.
```tsx
import {
ActionGroup,
ColumnLayout,
Heading,
Label,
RadioButton,
RadioGroup,
Section,
Text,
TextField,
} from "@mittwald/flow-react-components";
import {
Form,
SubmitButton,
typedField,
} from "@mittwald/flow-react-components/react-hook-form";
import { useForm, useWatch } from "react-hook-form";
export default () => {
const form = useForm<{
paymentMethod: "invoice" | "debit";
accountHolder: string;
iban: string;
}>({
defaultValues: {
paymentMethod: "invoice",
accountHolder: "",
iban: "",
},
});
const Field = typedField(form);
const watchedPaymentMethod = useWatch({
control: form.control,
name: "paymentMethod",
});
return (
);
}
```
---
# Multi-Upload
```tsx
import {
Button,
FileCard,
FileCardList,
FileDropZone,
FileField,
Heading,
IconUpload,
Section,
} from "@mittwald/flow-react-components";
import { useState } from "react";
export default () => {
const [files, setFiles] = useState([]);
return (
{
if (droppedFiles) {
setFiles((existingFiles) => [
...existingFiles,
...droppedFiles,
]);
}
}}
>
Dateien ablegen
{files.map((file) => (
setFiles(
files.filter((watched) => watched !== file),
)
}
/>
))}
);
}
```
---
# Verwendung
Verwende diesen Baustein, wenn Nutzer mehrere Dateien auf einmal auswählen und
vor dem Absenden noch prüfen oder einzelne wieder entfernen sollen. Für eine
einzelne Datei genügt ein [FileField](https://flow.mittwald.de/components/form-controls/file-field) oder
eine [FileDropZone](https://flow.mittwald.de/components/upload/file-drop-zone).
---
# Aufbau
- **Auswahl** – Eine [FileDropZone](https://flow.mittwald.de/components/upload/file-drop-zone) nimmt
Dateien per Drag-and-drop entgegen und enthält ein
[FileField](https://flow.mittwald.de/components/form-controls/file-field) für den Dateidialog.
`multiple` erlaubt mehrere Dateien auf einmal.
- **Dateiliste** – Eine [FileCardList](https://flow.mittwald.de/components/upload/file-card-list) aus
[FileCards](https://flow.mittwald.de/components/upload/file-card) zeigt die ausgewählten Dateien mit
Name, Typ und Größe; über `onDelete` entfernt der Nutzer einzelne vor dem
Upload.
- **Zwischenspeicher** – Die ausgewählten Dateien liegen in einem Array im State
(`File[]`) und werden erst beim Absenden übertragen; bis dahin lassen sie sich
prüfen und einzeln entfernen.
---
# Zeitintervalle
```tsx
import {
Content,
FieldError,
Label,
LabeledValue,
Option,
Section,
Select,
type SelectProps,
Separator,
Text,
TextField,
TimeField,
type TimeFieldProps,
} from "@mittwald/flow-react-components";
import CronTime from "cron-time-generator";
import { type FC, useMemo, useState } from "react";
import {
CronExpressionParser,
CronFieldCollection,
type HourRange,
type SixtyRange,
} from "cron-parser";
export default () => {
type Interval =
| "ONE_MINUTE"
| "FIVE_MINUTES"
| "THIRTY_MINUTES"
| "ONE_HOUR"
| "ONE_DAY"
| "SEVEN_DAYS"
| "FOURTEEN_DAYS"
| "THIRTY_DAYS"
| "CUSTOM";
const Intervals: Record<
Interval,
{
label: string;
toCronString: (lastCronString: string) => string;
allowTimeEdit?: boolean;
allowCustomEdit?: boolean;
}
> = {
ONE_MINUTE: {
label: "1 Minute",
toCronString: () => CronTime.everyMinute(),
},
FIVE_MINUTES: {
label: "5 Minuten",
toCronString: () => CronTime.every(5).minutes(),
},
THIRTY_MINUTES: {
label: "30 Minuten",
toCronString: () => CronTime.every(30).minutes(),
},
ONE_HOUR: {
label: "1 Stunde",
toCronString: () => CronTime.everyHour(),
},
ONE_DAY: {
label: "1 Tag",
toCronString: () => CronTime.everyDay(),
allowTimeEdit: true,
},
SEVEN_DAYS: {
label: "7 Tage",
toCronString: () => CronTime.every(7).days(),
allowTimeEdit: true,
},
FOURTEEN_DAYS: {
label: "14 Tage",
toCronString: () => CronTime.every(14).days(),
allowTimeEdit: true,
},
THIRTY_DAYS: {
label: "30 Tage",
toCronString: () => CronTime.every(30).days(),
allowTimeEdit: true,
},
CUSTOM: {
label: "Benutzerdefiniert",
allowCustomEdit: true,
toCronString: (lastCronString) => lastCronString,
},
};
const CronTimeSelector: FC<{
value?: string;
onChange: (cronValue: string) => void;
}> = ({ value = "", onChange }) => {
const [errorMessage, setErrorMessage] = useState<
string | null
>(null);
const [currentInterval, setInterval] =
useState("CUSTOM");
const parseCronExpression = (cron: string) => {
try {
const expression = CronExpressionParser.parse(cron);
setErrorMessage(null);
return expression;
} catch (error) {
if (error instanceof Error) {
setErrorMessage(error.message);
} else {
setErrorMessage(`invalid cron: ${cron}`);
}
return false;
}
};
const currentCronExpression = useMemo(
() => parseCronExpression(value),
[value],
);
const nextIntervals = useMemo(() => {
if (!currentCronExpression) {
return;
}
return currentCronExpression.take(5).map((time) => (
{time.toString()}
));
}, [currentCronExpression]);
const intervalSettings = Intervals[currentInterval];
const isValidCronExpression =
currentCronExpression && !errorMessage;
const handleIntervalChange: SelectProps["onChange"] = (
selectValue,
) => {
const newInterval = selectValue as Interval | null;
if (!newInterval) {
return;
}
setInterval(newInterval);
const intervalSettings = Intervals[newInterval];
if (onChange) {
onChange(intervalSettings.toCronString(value));
}
};
const handleTimeChange: TimeFieldProps["onChange"] = (
v,
) => {
if (onChange && currentCronExpression) {
const modified = CronFieldCollection.from(
currentCronExpression.fields,
{
hour: [(v ? v.hour : 0) as HourRange],
minute: [(v ? v.minute : 0) as SixtyRange],
},
);
onChange(modified.stringify());
}
};
const handleCustomCronChange = (customCron: string) => {
const newExpression = parseCronExpression(customCron);
if (newExpression && onChange) {
onChange(customCron);
}
};
return (
{intervalSettings.allowTimeEdit && (
)}
{intervalSettings.allowCustomEdit && (
{errorMessage && (
{errorMessage}
)}
)}
{isValidCronExpression && nextIntervals}
);
};
// only necessary because CronTimeSelector is an inline component
// which is a requirement in the demo page
const MemorizedCronTimeSelector = useMemo(
() => CronTimeSelector,
[],
);
const [currentCronString, setCronString] =
useState("0 0 * * *");
return (
<>
>
);
}
```
---
# Verwendung
Verwende diesen Baustein, wenn Nutzer festlegen, in welchem Intervall eine
Aktion wiederholt ausgeführt wird – etwa beim Konfigurieren eines Cronjobs.
Reduziere die Auswahl auf die im Kontext sinnvollen Intervalle, damit die
Entscheidung leicht fällt.
---
# Aufbau
- **Intervall-Auswahl** – Ein [Select](https://flow.mittwald.de/components/form-controls/select) schlägt
passende Zeitintervalle vor und hält die häufigen Fälle einfach. Sehr kleine
Intervalle sind oft weder nötig noch ressourcenschonend; weise bei Bedarf mit
einer [AccentBox](https://flow.mittwald.de/components/structure/accent-box) darauf hin.
- **Verfeinerung** – Je nach Auswahl wird dynamisch ein weiteres Feld
eingeblendet, etwa ein [TimeField](https://flow.mittwald.de/components/form-controls/time-field) für
die Uhrzeit. So bleibt die Oberfläche schlank und überfordert nicht. Dieses
Ein- und Ausblenden folgt der
[Progressive Disclosure](https://flow.mittwald.de/templates/bausteine/formular#varianten-und-abwandlung-progressive-disclosure)
des Formular-Bausteins.
- **Benutzerdefiniert** – Die Option „Benutzerdefiniert" gibt über ein
[TextField](https://flow.mittwald.de/components/form-controls/text-field) mit Cron-Syntax volle
Kontrolle; ungültige Ausdrücke werden direkt am Feld gemeldet.
- **Vorschau** – Ein [LabeledValue](https://flow.mittwald.de/components/content/labeled-value) zeigt die
nächsten Ausführungen zur Kontrolle. Kommuniziere die Zeitzone klar, in der
das Intervall gilt, sonst entstehen falsche Erwartungen.
---
# Action
Die Action umschließt einen Button und gibt visuelles Feedback bei asynchronen Aktionen.
> **mStudio-Extensions:** Importiere `Action` aus `@mittwald/flow-remote-react-components`. Die Beispiele auf dieser Seite importieren aus `@mittwald/flow-react-components`, das in einer Extension nicht rendert. Diese Props kommen remote nicht an: `wrapWith`.
```tsx
import {
Action,
Button,
} from "@mittwald/flow-react-components";
import { sleep } from "@/content/components/actions/action/examples/lib";
```
---
# Best Practices
- Nutze den Pending State nur für Vorgänge unter etwa fünf Sekunden. Für längere
Prozesse eignet sich eher eine
[Notification](https://flow.mittwald.de/components/status/notification).
- Setze Feedback States nur ein, wenn sie dem Nutzer einen Mehrwert bieten.
---
# Pending
Wenn eine asynchrone Aktion länger als 1000 Millisekunden dauert, wird der
**Pending** State angezeigt.
```tsx
import {
Action,
Button,
} from "@mittwald/flow-react-components";
import { sleepLong } from "@/content/components/actions/action/examples/lib";
```
---
# Feedback
Wenn ein visuelles Feedback nach einer Userinteraktion angezeigt werden soll,
kann das Property `showFeedback` genutzt werden.
Solange die Aktion läuft oder Feedback anzeigt, nimmt der Button bzw. das
MenuItem keine Klicks an und ist als `aria-disabled` markiert.
```tsx
import {
Action,
Button,
} from "@mittwald/flow-react-components";
{
// do nothing
}}
showFeedback
>
```
---
# Bestätigungsmodal
Um User vor unbeabsichtigten Aktionen zu schützen, kann ein Bestätigungsmodal
angezeigt werden. Dafür muss innerhalb der Action ein
[Modal](https://flow.mittwald.de/components/overlays/modal) mit dem Attribut `slot="actionConfirm"`
platziert werden.
```tsx
import {
Action,
ActionGroup,
Button,
Content,
Heading,
Modal,
} from "@mittwald/flow-react-components";
import { sleepLong } from "@/content/components/actions/action/examples/lib";
Projekt löschen
Wenn das Projekt "Test" einmal gelöscht ist,
kann es nicht wiederhergestellt werden.
```
---
# Verschachtelte Actions
Wenn mehrere Aktionen auf eine Userinteraktion folgen, können Actions auch
ineinander verschachtelt werden.
```tsx
import {
Action,
Button,
} from "@mittwald/flow-react-components";
console.log("close modal")}>
console.log("save")}>
```
---
# In MenuItems
Actions können auch um `MenuItems` gelegt werden, um deren State zu steuern.
```tsx
import {
Action,
Button,
ContextMenuTrigger,
ContextMenu,
MenuItem,
} from "@mittwald/flow-react-components";
import { sleepLong } from "@/content/components/actions/action/examples/lib";
```
---
# Actions abbrechen
Wenn eine Action abgebrochen werden soll, ohne einen Fehler auszulösen, kann die
Funktion `abortAction` aus dem Action-Modul verwendet werden. Diese Funktion
wirft einen speziellen Fehler, der von der Action-Component erkannt wird.
Dadurch kann die Action sauber abgebrochen werden, ohne dass unerwünschte
Fehlermeldungen oder Seiteneffekte auftreten.
Alle nachfolgenden Funktionen in der Action-Execution-Logik, die nach dem Aufruf
von `abortAction` definiert sind, werden nicht mehr ausgeführt. Das bedeutet,
dass der gesamte Ablauf der Action sofort gestoppt wird, sobald `abortAction`
aufgerufen wird.
Umschließende Actions bei einer Verschachtelung von Actions werden ebenfalls
abgebrochen, wenn `abortAction` in einer inneren Action aufgerufen wird.
```tsx
import { sleep } from "@/content/components/actions/action/examples/lib";
import {
abortAction,
Action,
Button,
} from "@mittwald/flow-react-components";
{
console.log("Outer action");
}}
>
{
await sleep();
console.log("Inner action");
abortAction();
}}
>
```
---
# Properties
| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `actionModel` | `ActionModel` | - | An existing action model to use instead of creating a new one. Lets several components share one action state. |
| `break` | `boolean` | `false` | Stops the execution here: parent actions are not executed. |
| `children` | `ReactNode` | - | - |
| `closeModal` | `boolean \| CloseModalOptions` | - | Whether the surrounding modal is closed when the action is triggered. |
| `closeOverlay` | `boolean \| keyof FlowComponentPropsTypes \| OverlayController \| CloseOverlayOptions` | - | The overlay to close when the action is triggered. `true` closes the nearest enclosing overlay, whichever component it is — a name still wins, so `closeOverlay="Modal"` inside a `Popover` closes the outer modal. There is no `openOverlay={true}` counterpart: the nearest overlay is the one this action renders inside, so it is open by definition. |
| `openModal` | `boolean` | - | Whether the surrounding modal is opened when the action is triggered. |
| `openOverlay` | `keyof FlowComponentPropsTypes \| OverlayController` | - | The overlay to open when the action is triggered. |
| `showFeedback` | `boolean` | - | Whether success feedback is shown after the action succeeded. Asynchronous actions show feedback by default; set `false` to suppress it. |
| `skip` | `number \| boolean` | `false` | The number of parent actions skipped when the action is triggered. `true` skips one. |
| `toggleModal` | `boolean` | - | Whether the surrounding modal is opened or closed when the action is triggered. |
| `toggleOverlay` | `keyof FlowComponentPropsTypes \| OverlayController` | - | The overlay to open or close when the action is triggered. |
| `wrapWith` | `ReactElement>` | - | A React element the component is wrapped with. The element is cloned and receives the component as its only child — useful to render the component inside a link, a tooltip trigger or any other wrapper without changing the surrounding markup. `null` takes a wrapper a surrounding props context set back off, the way `tunnel: null` does for a tunnel. |
### Events
| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `onAction` | `ActionFn` | - | The function executed when the action is triggered. Returning a promise puts the action into its pending and feedback states automatically. |
---
# ActionGroup
Die ActionGroup gruppiert zusammengehörige Actions.
> **mStudio-Extensions:** Importiere `ActionGroup` aus `@mittwald/flow-remote-react-components`. Die Beispiele auf dieser Seite importieren aus `@mittwald/flow-react-components`, das in einer Extension nicht rendert. Diese Props kommen remote nicht an: `wrapWith`.
```tsx
import {
ActionGroup,
Button,
} from "@mittwald/flow-react-components";
```
---
# Best Practices
- Für die Buttons in der ActionGroup gelten die
[Best Practices des Buttons](https://flow.mittwald.de/components/actions/button).
- Hebe pro Seite möglichst nur eine Hauptaktion hervor. So erkennt der User den
primären nächsten Schritt eindeutig.
---
# Slots
Die [Actions](https://flow.mittwald.de/components/actions/action) werden von der ActionGroup automatisch
in die Slots `primary`, `secondary` und `abort` sortiert. `primary` steht am
Ende der ActionGroup, `secondary` direkt davor und `abort` am Anfang. Die
Reihenfolge im Code spielt dabei keine Rolle.
Hat ein [Button](https://flow.mittwald.de/components/actions/button) keinen `slot`, entscheidet seine
Color: Primary (Standard), Success und Danger landen in `primary`, alle anderen
Colors in `abort`. Ein Button mit der Color Secondary, der nicht abbricht – etwa
„Nur prüfen“ oder „Als Entwurf speichern“ –, braucht deshalb `slot="secondary"`.
Sonst landet er neben „Abbrechen“ am Anfang der ActionGroup.
```tsx
import {
ActionGroup,
Button,
} from "@mittwald/flow-react-components";
```
## Reihenfolge beibehalten
Mit `preserveOrder` verzichtet die ActionGroup auf die automatische Sortierung
in die Slots `primary`, `secondary` und `abort`. Die Actions bleiben in der
Reihenfolge, in der sie im Code stehen.
```tsx
import {
ActionGroup,
Button,
} from "@mittwald/flow-react-components";
```
---
# Sizes
Mit `size` werden die Buttons und Links in der ActionGroup einheitlich
dimensioniert.
```tsx
import {
ActionGroup,
Button,
Link,
} from "@mittwald/flow-react-components";
Passwort vergessen?
```
---
# Kombiniere mit …
## Link
Kombiniere Buttons in der ActionGroup mit einem
[Link](https://flow.mittwald.de/components/navigation/link), um textuelle Nebenaktionen anzubieten.
```tsx
import {
ActionGroup,
Button,
Link,
} from "@mittwald/flow-react-components";
Passwort vergessen?
```
---
# Reihenfolge der Buttons
Es kann vorkommen, dass sich durch Rerendering die Reihenfolge der Buttons
ändert, wenn z. B. Inhalte nachgeladen werden. Um das zu verhindern, sollte um
diese `Inhalte` ein Suspense gelegt werden.
```
...
```
---
# Properties
| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | - | - |
| `className` | `string` | - | The elements class name. |
| `preserveOrder` | `boolean` | `false` | When set, the buttons are not sorted into the slots `primary`, `secondary` and `abort` but stay in source order. Use it when the actions have no such hierarchy, e.g. a row of equal actions or icon buttons. |
| `size` | `"s" \| "m"` | `"m"` | The size of the buttons and links inside the action group. |
| `spacing` | `"s" \| "m"` | `"s"` | The spacing between the buttons inside the action group. |
| `wrapWith` | `ReactElement>` | - | A React element the component is wrapped with. The element is cloned and receives the component as its only child — useful to render the component inside a link, a tooltip trigger or any other wrapper without changing the surrounding markup. `null` takes a wrapper a surrounding props context set back off, the way `tunnel: null` does for a tunnel. |
---
# Design Tokens
## Größen & Stile
| Token | Wert |
| --- | --- |
| `--action-group--spacing--m` | `16px` |
| `--action-group--spacing--s` | `8px` |
---
# Button
Buttons werden verwendet, um Aktionen auf einer Seite oder in einem Formular auszulösen.
> **mStudio-Extensions:** Importiere `Button` aus `@mittwald/flow-remote-react-components`. Die Beispiele auf dieser Seite importieren aus `@mittwald/flow-react-components`, das in einer Extension nicht rendert. Diese Props kommen remote nicht an: `render`, `style`, `wrapWith`.
```tsx
import { Button } from "@mittwald/flow-react-components";
```
---
# Best Practices
- Beschrifte den Button kurz und eindeutig. Ergänze ein `aria-label`, wenn der
Kontext nicht klar ist, etwa bei einem reinen Icon-Button oder wenn der Text
allein nicht ausreicht.
- Richte die visuelle Hierarchie nach der Wichtigkeit der Aktion. Nutze dafür
die verschiedenen Varianten und Colors.
- Gib dem User nach der Aktion ein visuelles Feedback. Über die
[Action](https://flow.mittwald.de/components/actions/action) lässt sich dieses Feedback steuern.
## Button vs. Link
Verwende für Navigation bevorzugt Links und für Aktionen Buttons. Eine Ausnahme
können hervorgehobene
[Call-to-Actions](https://flow.mittwald.de/components/navigation/link#kombiniere-mit-button) sein, bei
denen ein Button-ähnliches Erscheinungsbild für eine Navigation sinnvoll ist.
**Verwende Buttons, um z. B. ...**
- eine Aktion auszuführen oder ein Event zu starten, ohne die Seite zu verlassen.
- Formulare zu speichern oder zurückzusetzen.
- in einem [Modal](https://flow.mittwald.de/components/overlays/modal) Aktionen auszuwählen.
**Verwende Links, um z. B. ...**
- zu einer anderen Seite zu navigieren.
- zu einem Anker auf der Seite zu scrollen.
- eine externe Seite zu öffnen.
- einen Download zu starten.
---
# Variants
Verwende die unterschiedlichen Varianten, um Buttons in einer Oberfläche eine
visuelle Hierarchie zu geben:
- **Solid**, um Hauptaktionen hevrozuheben, die eine hohe Aufmerksamkeit
erfordern.
- **Soft**, um Nebenaktionen abzuschwächen, die visuell keine hohe
Aufmerksamkeit erfordern.
- **Outline**, um eine Aktion darzustellen, die klar als solche erkennbar sein
soll, aber weniger Aufmerksamkeit als ein gefüllter Button beansprucht.
- **Plain**, um weniger wichtige oder zurückhaltende Aktionen darzustellen.
```tsx
import { Button } from "@mittwald/flow-react-components";
<>
>
```
---
# Color
Farben unterstützen die visuelle Hierarchie zusätzlich. Verwende:
- **Primary**, um Hauptaktionen standardmäßig visuell hervorzuheben.
- **Secondary**, um Nebenaktionen abzuschwächen und neutral darzustellen.
- **Success**, um die positive Bedeutung einer Aktion zu stärken. Diese Farbe
wird daher vor allem im Kontext von Erstellungs-, Speicher- oder
Bestellvorgängen verwendet.
- **Danger**, um negative Konsequenzen einer Aktion zu verdeutlichen. Sie wird
beispielsweise für Lösch- oder Kündigungsaktionen verwendet.
```tsx
import { Button } from "@mittwald/flow-react-components";
<>
>
```
## Light und Dark
Um auf farbigen Hintergründen ausreichend Kontrast zu erreichen, können Buttons
auch in Light und Dark eingesetzt werden. Mehr zur richtigen Verwendung von
Light und Dark findest du in den Foundations unter
[Color](https://flow.mittwald.de/foundations/design/colors#light-und-dark-color).
```tsx
import { Button } from "@mittwald/flow-react-components";
<>
>
```
```tsx
import { Button } from "@mittwald/flow-react-components";
<>
>
```
---
# Sizes
**Medium** ist die Standardgröße und wird für die meisten Buttons verwendet.
**Small** kann genutzt werden, wenn weniger Platz vorhanden ist. Vermeide es,
unterschiedliche Button-Größen direkt nebeneinander zu verwenden.
```tsx
import { Button } from "@mittwald/flow-react-components";
<>
>
```
---
# States
**Disabled** zeigt an, dass eine Aktion nicht ausgeführt werden kann. Achte
darauf, dass aus dem Kontext klar wird, warum die Aktion nicht zur Verfügung
steht.
Die States **Pending**, **Succeeded** und **Failed** geben dem User zusätzliches
visuelles Feedback darüber, dass im Hintergrund eine Aktion läuft, erfolgreich
abgeschlossen wurde oder fehlgeschlagen ist. Um diese States zu steuern, kann
die [Action](https://flow.mittwald.de/components/actions/action)-Komponente genutzt werden.
```tsx
import { Button } from "@mittwald/flow-react-components";
<>
>
```
---
# Content
Der Content-Bereich eines Buttons kann entweder Text, ein
[Icon](https://flow.mittwald.de/components/content/icon) oder [Text](https://flow.mittwald.de/components/content/text) mit einem
Icon am Ende enthalten. Achte in allen Fällen darauf, dass Text und Icon die
Aktion eindeutig beschreiben.
## Icon
Wenn ausschließlich ein Icon verwendet wird, sollte das `aria-label` den
Verwendungszweck beschreiben. Ein [Tooltip](https://flow.mittwald.de/components/overlays/tooltip) kann
zusätzlich verdeutlichen, wofür die Aktion gedacht ist.
```tsx
import {
Button,
IconHome,
} from "@mittwald/flow-react-components";
```
## Text + Icon
Der Default ist Text ohne Icon, das hält die Oberfläche ruhig. Ein Icon kommt
nur dazu, wenn es etwas beiträgt, das der Text allein nicht abdeckt:
- **Verhalten:** Das Icon zeigt, wie sich der Button verhält. Ein Chevron-Icon
zeigt zum Beispiel an, dass sich dahinter ein
[ContextMenu](https://flow.mittwald.de/components/actions/context-menu) verbirgt.
- **Erkennung:** Das Icon steht produktweit und ausschließlich für eine
Feature-Familie, nach der User scannen, z. B. das AI-Icon neben „AI Agent
verbinden".
Wiederholt das Icon nur das Wort (etwa eine Diskette neben „Speichern"), bringt
es nur Unruhe. Im Zweifel weglassen.
```tsx
import {
Button,
IconChevronDown,
Text,
} from "@mittwald/flow-react-components";
```
---
# Kombiniere mit …
## Combine
Benutze die [Combine](https://flow.mittwald.de/components/structure/combine)-Component, um deinen Button
neben Input-Feldern zu platzieren.
```tsx
import {
Combine,
Button,
Label,
TextField,
} from "@mittwald/flow-react-components";
```
## ActionGroup
Benutze die [ActionGroup](https://flow.mittwald.de/components/actions/action-group)-Component, um
zusammengehörige Aktionen zu gruppieren und automatisch in eine konsistente
Hierarchie (Haupt- und Nebenaktion) zu bringen.
```tsx
import {
ActionGroup,
Button,
} from "@mittwald/flow-react-components";
```
## Action
Benutze die [Action](https://flow.mittwald.de/components/actions/action)-Component, um die States eines
Buttons zu steuern.
```tsx
import {
Action,
Button,
} from "@mittwald/flow-react-components";
new Promise((resolve) => setTimeout(resolve, 1500))
}
>
```
---
# Properties
| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `autoFocus` | `boolean` | - | Whether the element should receive focus on render. |
| `children` | `ReactNode` | - | The children of the component. A function may be provided to alter the children based on component state. |
| `className` | `ClassNameOrFunction` | `'react-aria-Button'` | The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state. |
| `color` | `"dark" \| "light" \| "dark-static" \| "light-static" \| "success" \| "danger" \| "primary" \| "secondary"` | `"primary"` | The color of the button. |
| `dir` | `string` | - | - |
| `excludeFromTabOrder` | `boolean` | - | Whether to exclude the element from the sequential tab order. If true, the element will not be focusable via the keyboard by tabbing. This should be avoided except in rare scenarios where an alternative means of accessing the element or its functionality via the keyboard is available. |
| `form` | `string` | - | The `