Einstellungen
Einstellungen für ein KI-Produkt, gestaltet wie bei Cursor und Claude. Eine gefüllte Seitenleiste mit Suche, Gruppen und externen Links, Karten mit Zeilen, dezenten Pickern und verschachtelten Optionen, eine dunkle Speicher-Insel, die nur erscheint, wenn sich etwas geändert hat, ⌘S zum Speichern, Feldfehler aus deinen Prüfungen oder deinem Server und Ladezustände in der Form des Inhalts.
Cursor, Claude und Codex haben sich alle auf dieselbe Einstellungsseite geeinigt: eine gefüllte Seitenleiste mit Suche und einer Handvoll gruppierter Bereiche und rechts Cards aus Zeilen mit Label und Beschreibung links und einem dezenten Steuerelement rechts. Settings ist diese Seite. Es hält deine Bereiche und kümmert sich um die Dinge, die jede Einstellungsseite falsch macht: verlorene Änderungen, doppeltes Speichern und der Sprung von einer Seitenleiste am Desktop zu einer Liste auf dem Smartphone.
Zeilen nehmen jedes Steuerelement. SettingsSelect ist der kompakte Werte-Picker, den diese Apps nutzen, ein kleiner Button mit Umriss, der ein Auswahlmenü öffnet, und SettingsNumber ist ein Stepper, den du zum Wiederholen halten kannst. Beide tragen den Namen ihrer Zeile, sodass Screenreader „Chat font, Serif“ hören. SettingsLink ist eine Zeile, die etwas anderes öffnet, mit einem Chevron oder einem Pfeil für Links, die die App verlassen. SettingsNested gleitet abhängige Optionen unter einem Switch auf, etwa Netzwerkzugriff unter Run code. Die Suche filtert die Seitenleiste nach Label, Beschreibung und Keywords, und Enter öffnet den ersten Treffer. SettingsChoice macht aus einer Auswahl Bildkarten, sodass Leute ein Theme oder eine Dichte danach wählen, wie es aussieht.
Nichts wird gespeichert, bis du es sagst. Sobald ein Wert vom Gespeicherten abweicht, steigt eine dunkle Insel mit Discard und Save von unten auf, und der Eintrag des Bereichs in der Seitenleiste erhält einen Punkt. Ändere ihn zurück, und die Leiste verschwindet. Versuchst du, einen anderen Bereich zu öffnen, auf dem Smartphone zurückzugehen oder den Tab zu schließen, wird der Wechsel blockiert: Die Leiste wackelt und sagt, du sollst zuerst speichern oder verwerfen, und der Browser fragt vor dem Schließen des Tabs. ⌘S oder Strg+S speichert von überall.
Beim Speichern zeigt der Button den Fortschritt, dann schrumpft die Insel zu einem Saved-Häkchen und gleitet weg. Schlagen deine Prüfungen fehl, zeigen die Felder ihre Fehler, der Fokus springt zum ersten, und die Leiste sagt, wie viele zu beheben sind. Sagt der Server nein, gib Fehler für die Felder zurück oder wirf einen Fehler, und der Entwurf bleibt exakt wie getippt. Tippe weiter, während gespeichert wird, und die Leiste bleibt für die neueren Änderungen stehen.
Auf dem Smartphone wird die Seitenleiste zu einer gruppierten Liste mit Beschreibungen und Chevrons. Ein Tipp auf einen Bereich gleitet ihn mit einem Zurück-Button über die Liste, und der Fokus springt zu seiner Überschrift. Während die Daten eines Bereichs laden, zeigt er ein Skeleton in der Form von Switch-Zeilen oder dein eigenes über die skeleton-Prop und bei einem Ladefehler einen Fehler mit Try again.
Die Pro-Registry zu components.json hinzufügen
components.json Token hinzufügen
Erstelle auf deiner Kontoseite einen Token und trage ihn in
.env.localalsHEXTAUI_PRO_TOKENein.Den Block hinzufügen
pnpm dlx shadcn@latest add @hextaui-pro/settings
Einen Bereich an deine API anbinden
useSettingsForm hält einen Entwurf der Werte, die du übergibst. Gib Feldfehler aus onSave zurück, um sie unter dem Feld zu zeigen, oder wirf einen Fehler, um die Meldung in der Speicherleiste zu zeigen. In beiden Fällen bleibt der Entwurf.
Eine Route pro Bereich
Steuere den aktiven Bereich mit value und onValueChange, um jedem Bereich eine eigene URL zu geben. Die Shell blockiert den Wechsel weiterhin, solange etwas ungespeichert ist, sodass onValueChange nur feuert, wenn das Verlassen sicher ist.
Laden und Fehler
Übergib status, während die Daten eines Bereichs laden. Das Skeleton wartet 150 ms, damit schnelle Ladevorgänge nie aufblitzen, und ein Fehlerzustand bietet Try again über onRetry an.
Aufbau
Die Teile, die du zusammensetzt, von außen nach innen.
| Part | Beschreibung |
|---|---|
SettingsShell | Die Seite: die Bereichsnavigation, die Inhaltsspalte, die Speicherleiste und der Schutz vor dem Verlassen mit ungespeicherten Änderungen. |
SettingsSection | Ein Bereich. Rendert nur, solange er geöffnet ist, mit Überschrift, optionalen Aktionen und Lade- oder Fehlerzuständen. |
SettingsGroup | Eine betitelte Card aus Zeilen, mit optionaler Fußzeile für einen Hinweis zur Gruppe. |
SettingsRow | Ein Label, eine Beschreibung und ein Steuerelement, für Screenreader miteinander verbunden, mit dem Feldfehler darunter. |
SettingsSelect | Ein dezenter Picker für einen Wert aus einer kurzen Liste. |
SettingsLink | Eine Zeile, die eine Seite, einen Dialog oder einen externen Link öffnet. |
SettingsNested | Abhängige Optionen, die aufgleiten, solange ein übergeordneter Switch an ist. |
SettingsNumber | Ein Zahlen-Stepper mit − und +, die beim Halten wiederholen, gebaut auf dem Number Field von Base UI. |
SettingsChoice | Bildkarten zur Wahl einer Option, etwa eines Themes oder einer Dichte, mit Radio-Semantik. |
SettingsSkeleton | Der Lade-Platzhalter, konfigurierbar über Zeilen pro Gruppe und Form des Steuerelements. |
useSettingsForm | Der Entwurf für einen Bereich. Verfolgt, was sich geändert hat, validiert, speichert und verbindet den Bereich mit der Speicherleiste. |
useSettingsNavigate | Öffnet einen Bereich aus dem Inhalt heraus, genauso abgesichert wie die Seitenleiste. |
SettingsShell
Akzeptiert auch alle div-Props.
| Prop | Typ | Standard |
|---|---|---|
sections{ id, label, description?, icon?, group?, keywords?, href? }. Aufeinanderfolgende Items mit derselben group teilen sich eine Überschrift. keywords helfen der Suche, einen Bereich zu finden, und href macht das Item zu einem externen Link. | SettingsSectionItem[] | – |
valueDer geöffnete Bereich, wenn du ihn kontrollierst. | string | – |
defaultValueDer zuerst geöffnete Bereich. | string | first section |
onValueChangeWird aufgerufen, wenn jemand einen anderen Bereich öffnet. Wird nie aufgerufen, solange etwas ungespeichert ist oder gespeichert wird. | (value: string) => void | – |
titleDie Seitenüberschrift über der Navigation und das Label des Zurück-Buttons auf Smartphones. | ReactNode | "Settings" |
descriptionEine Zeile unter dem Titel. | ReactNode | – |
navHeaderInhalt oben in der Seitenleiste, etwa ein Back-Link zur App. | ReactNode | – |
searchableFügt über den Bereichen ein Suchfeld hinzu. | boolean | false |
navFooterInhalt, der unten in der Seitenleiste angeheftet ist, etwa der angemeldete Nutzer. | ReactNode | – |
groupLabelsZeigt den Namen jeder Gruppe über ihr. Schalte es aus, um Gruppen nur durch Abstand zu trennen; die Namen beschriften die Gruppen weiterhin für Screenreader. | boolean | true |
SettingsSection
Akzeptiert auch alle section-Props.
| Prop | Typ | Standard |
|---|---|---|
idEntspricht einer ID in sections. | string | – |
titleDie Überschrift. | ReactNode | the section's label |
descriptionDie Zeile unter der Überschrift. | ReactNode | the section's description |
actionsButtons neben der Überschrift. | ReactNode | – |
statusZeigt ein Skeleton oder einen Fehler statt der children. | "ready" | "loading" | "error" | "ready" |
skeletonWas angezeigt wird, solange status loading ist. | ReactNode | <SettingsSkeleton /> |
errorDie Meldung für den Fehlerzustand. | ReactNode | – |
onRetryFügt dem Fehlerzustand Try again hinzu. | () => void | – |
| Prop | Typ | Standard |
|---|---|---|
titleÜberschrift über der Card. | ReactNode | – |
descriptionEine gedämpfte Zeile unter der Überschrift, die sagt, worum es in der Gruppe geht. | ReactNode | – |
footerEin gedämpfter Streifen am unteren Rand der Card, für Hinweise wie das, was eine Änderung betrifft. | ReactNode | – |
| Prop | Typ | Standard |
|---|---|---|
labelBeschriftet das Steuerelement in der Zeile. | ReactNode | – |
descriptionHilfetext, der mit dem Steuerelement vorgelesen wird. | ReactNode | – |
errorMarkiert das Steuerelement als ungültig und zeigt die Meldung unter der Zeile. | string | – |
layoutauto setzt das Steuerelement neben das Label, wenn die Card breit ist, und darunter, wenn sie schmal ist. inline hält es neben dem Label, für Switches. stacked setzt es immer darunter, für Textareas. | "auto" | "inline" | "stacked" | "auto" |
disabledDeaktiviert das Feld der Zeile. | boolean | false |
SettingsSelect
Akzeptiert auch alle Button-Props.
| Prop | Typ | Standard |
|---|---|---|
valueDer gewählte Wert. | string | – |
onValueChangeWird mit dem neuen Wert aufgerufen. | (value: string) => void | – |
optionsDie Auswahlmöglichkeiten, der Reihe nach. | { value, label }[] | – |
SettingsChoice
Eine Radio-Gruppe, sodass Pfeiltasten zwischen Cards wechseln. Akzeptiert auch alle RadioGroup-Props von Base UI.
| Prop | Typ | Standard |
|---|---|---|
valueDie gewählte Option. | string | – |
onValueChangeWird mit der neuen Option aufgerufen. | (value: string) => void | – |
optionsDas Bild jeder Card und der Name darunter. | { value, label, preview }[] | – |
columnsCards pro Zeile. 4 wird bei schmaler Zeile zu 2. | 2 | 3 | 4 | 3 |
ratio16:10-Vorschauen, oder 2:1 für flachere. | "card" | "wide" | "card" |
SettingsNumber
Akzeptiert auch alle NumberField.Root-Props von Base UI, etwa format und smallStep.
| Prop | Typ | Standard |
|---|---|---|
valueDie aktuelle Zahl. | number | null | – |
onValueChangeWird aufgerufen, wenn sich die Zahl ändert. | (value: number | null) => void | – |
minNiedrigster Wert. Der −-Button wird dort deaktiviert. | number | – |
maxHöchster Wert. Der +-Button wird dort deaktiviert. | number | – |
stepWie stark jeder Druck oder jede Pfeiltaste den Wert ändert. | number | 1 |
SettingsLink
Akzeptiert auch alle Anchor-Props. Rendert einen Button, wenn es kein href gibt.
| Prop | Typ | Standard |
|---|---|---|
labelDer Titel der Zeile. | ReactNode | – |
descriptionEine Zeile unter dem Titel. | ReactNode | – |
externalÖffnet href in einem neuen Tab und zeigt einen Pfeil statt eines Chevrons. | boolean | false |
| Prop | Typ | Standard |
|---|---|---|
openZeigt die Optionen. Meist der Wert des übergeordneten Switches. | boolean | – |
| Prop | Typ | Standard |
|---|---|---|
groupsWie viele Zeilen jede Platzhaltergruppe hat. | number[] | [3, 2] |
controlDie Form rechts in jeder Zeile. | "switch" | "select" | "input" | "switch" |
useSettingsForm
Gibt { values, setValue, errors, dirty, status, save, discard } zurück.
| Prop | Typ | Standard |
|---|---|---|
valuesDas jetzt Gespeicherte. Ändert es sich und gibt es keine Änderungen, folgt der Entwurf ihm. | Values | – |
onSaveSpeichert den Entwurf. Gib { field: message } zurück, um Feldfehler zu zeigen, oder wirf einen Fehler, um die Meldung in der Speicherleiste zu zeigen. | (values) => void | errors | Promise<void | errors> | – |
validateLäuft vor onSave. Jeder Fehler stoppt das Speichern und fokussiert das erste ungültige Feld. | (values) => errors | undefined | – |
useSettingsNavigate
Gibt eine Funktion zurück, die von überall in der Shell einen Bereich öffnet, etwa den Open-Button eines Banners. Sie respektiert ungespeicherte Änderungen genauso wie die Seitenleiste.
| Prop | Typ | Standard |
|---|---|---|
navigateÖffnet den Bereich oder lässt die Speicherleiste wackeln, wenn etwas ungespeichert ist. | (id: string) => void | – |
| Taste | Aktion |
|---|---|
| Tab | Wechselt durch die Navigation, dann den Bereich, dann die Speicherleiste, wenn sie offen ist. |
| Enter | Öffnet den fokussierten Bereich. |
| ↑↓ | Ändert in einem Stepper die Zahl um einen Schritt. Shift bewegt um zehn. |
| Enter | Öffnet im Suchfeld den ersten passenden Bereich. Escape leert die Suche. |
| ⌘S | Speichert, solange etwas ungespeichert ist. Strg+S unter Windows und Linux. |
- Die Navigation ist eine Landmark, und der geöffnete Bereich ist als aktuelle Seite markiert.
- Jeder Bereich ist eine Region, benannt nach seiner Überschrift. Auf Smartphones springt der Fokus beim Öffnen eines Bereichs zur Überschrift und beim Zurückgehen zu seiner Zeile.
- Zeilen nutzen Field, sodass Labels, Beschreibungen und Fehler mit dem Steuerelement verbunden sind.
- Blockierte Navigation wird höflich angesagt, und ein fehlgeschlagenes Speichern wird als Alert angesagt.
- Die Speicherleiste und jedes verborgene Panel sind inert, sodass sie aus der Tab-Reihenfolge und vor Screenreadern verborgen sind.
- Bei reduzierter Bewegung blenden Panels aus, statt zu gleiten, und das Wackeln der Speicherleiste wird zu einem Ring.
Gebaut mit
Die kostenlosen HextaUI-Komponenten, aus denen Settings besteht. Jede lässt sich einzeln installieren.
Code
6 Dateien, hinzugefügt zu components/blocks/settings.