ComponentsPage layouts

Settings layout

A shared skeleton for settings pages: section nav with scroll-spy, titled sections of label | control rows, a sticky save bar that rises when there are unsaved changes, and a danger zone. Rows act like a Field, so kit controls are labelled and described without extra ids. Not bound to a form library — pass dirty from your form state.

Field context · IntersectionObserver (scroll-spy) · container queries components/settings-layout.tsxtheme · material · accent · density · font

General

Basic information about the site, shown to visitors and search engines.

Shown in the browser tab and in search results.

One line under the site name.

Visitors see a holding page; signed-in editors still see the site.

Reading

Ask search engines not to index this site.

Notifications

Email the post's author.

Send a daily digest to the site owner.

Danger zone

These actions can't be undone.

Removes 128 posts, 418 media files and all form submissions.

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { SettingsLayout, SettingsNav, SettingsSection, SettingsRow, SettingsSaveBar } from "@vamos-ui/react";

<SettingsLayout nav={<SettingsNav items={[{ id: "general", label: "General" }, { id: "danger", label: "Danger zone", tone: "danger" }]} />}>
  <SettingsSection id="general" title="General" description="Basic information about the site.">
    <SettingsRow label="Site name" description="Shown in the browser tab.">
      <Input {...register("name")} />
    </SettingsRow>
    <SettingsRow label="Maintenance mode" layout="inline"><Switch /></SettingsRow>
  </SettingsSection>
  <SettingsSection id="danger" tone="danger" title="Danger zone">…</SettingsSection>
  <SettingsSaveBar dirty={formState.isDirty} saving={formState.isSubmitting} onSave={handleSubmit(save)} onReset={() => reset()} />
</SettingsLayout>

Examples

Save bar

The bar rises while dirty, saving or error, announces itself politely and saves with ⌘/Ctrl+S. Every third save here fails to show the error state.

Stage: Proposal

Deals in this stage show in the forecast with this weight.

Used for the weighted pipeline value.

%

Highlight deals with no activity for 14 days.

Split sections

layout="split" puts the title and description in a start column on wide layouts and stacks them when narrow.

Annual leave

Applies to full-time employees in Türkiye. Part-time allowances are pro-rated.

Before seniority bonuses.

days

Up to 5 days into the next year.

Sick leave

Requests over 2 days need a doctor's note.

Changes apply to requests created from tomorrow.

Danger zone

tone="danger" with a typed confirmation from useConfirm({ requireText }).

You're the only owner

Transfer ownership before you leave the workspace.

Danger zone

Irreversible and destructive actions.

Make another admin the owner of this workspace.

Type the workspace name to confirm.

Loading and read-only

loading renders skeleton rows; readOnly disables every control through the row's field context — pair it with an Alert in notice.

Profile

API

SettingsLayout

PropTypeDefault
nav
A SettingsNav: a start column on wide layouts, a scrolling strip below 672px of layout width. Rows stack when their card is under 32rem.
ReactNode—
width
64rem or 80rem overall.
"md" | "lg""md"
readOnly
Disables every row control.
booleanfalse
notice
Banner above the first section.
ReactNode—
stickyTop
Where the nav sticks.
number | string"var(--topbar-h)"
labels
Overrides for the settings namespace.
SettingsLayoutLabels—

SettingsNav

PropTypeDefault
items
Anchors (#id, scroll-spy) or route links (href). permission is checked with can().
{ id; label; href?; icon?; badge?; permission?; tone? }[]—
value · defaultValue · onValueChange
Active item.
string—
scrollSpy
Follow the section in view.
booleantrue
aria-label
Name of the nav.
string"Settings sections"

SettingsSection

PropTypeDefault
id
Anchor for SettingsNav.
string—
title · description
Heading (<section aria-labelledby>) and help text.
ReactNode—
layout
Title above the card, or in a start column.
"stacked" | "split""stacked"
tone
Danger hairline and title.
"default" | "danger""default"
footer
Card footer — a hint or a section button.
ReactNode—
loading
Skeleton rows (number = row count).
boolean | number—
headingLevel
Heading level.
2 | 32

SettingsRow

PropTypeDefault
label · description
Wired to the control (label for, aria-describedby).
ReactNode—
layout
Side by side (stacks when narrow), control at the end (switches), or full width.
"split" | "inline" | "stacked""split"
htmlFor
Control id. Default: generated and passed to kit controls.
string—
badge
Next to the label.
ReactNode—
error
Error text; marks kit controls invalid.
ReactNode—
disabled
Disables kit controls in the row.
boolean—

SettingsSaveBar

PropTypeDefault
dirty
Unsaved changes — shows the bar.
boolean—
saving
Save button loading.
boolean—
onSave · onReset
Save (also ⌘/Ctrl+S) and Discard.
() => void—
message
Replace "You have unsaved changes".
ReactNode—
error
Save failed: danger tone and an alert.
ReactNode | true—
blockNavigation
beforeunload warning while dirty.
booleantrue
shortcuts
⌘/Ctrl+S saves.
booleantrue
saveLabel
Save button text.
string"Save changes"