Skip to content

Patterns · Layout

SettingsSection

Titled card of settings rows with a save footer and dirty state, loading and no-access states, and a danger-zone variant.

@rookcom/monarch-reactPattern

Profile settings

Profile

How you appear to your teammates.

Shown on comments and assignments.

Usage examples

Save footer with dirty state

Danger zone

Loading and no access

Purpose

Groups related settings under a heading and description, in a card. SettingsRow lays out one setting and wires its label, description and error to the control; SettingsActionRow lays out one action such as "Delete workspace". The footer can be the standard Save/Discard bar driven by a dirty flag.

Use when

  • Settings and preferences pages.
  • A form section that saves as a unit and should say when it has unsaved changes.
  • A destructive zone (delete, transfer) with tone="danger" and SettingsActionRow.

Avoid when

  • Data-entry forms that are not settings: use Field directly.
  • Settings that apply immediately (a single switch list): use SwitchSettingsList.

Design principles

  • Label and help on one side, control on the other, so long settings pages scan quickly.
  • Each row is a Field, so accessibility wiring is automatic.
  • Save/Discard live in the section footer, not per row, and stay disabled until something changed.
  • The product owns the draft and the save; the section only shows the state it is given.

API reference

import { SettingsActionRow, SettingsRow, SettingsSection } from '@rookcom/monarch-react';
ExportKindRenderingRoot class
SettingsActionRowcomponentclient.mn-settings-section
SettingsRowcomponentclient.mn-settings-section
SettingsSectioncomponentclient.mn-settings-section

SettingsSection renders a <section> labelled by its heading; native attributes pass through.

SettingsRow is a Field: the control child receives the label, description, required, disabled and invalid wiring. invalid defaults to true when error is given.

With onSave, the footer is a polite status line, any actions, Discard (onCancel) and Save. Without it, the footer is actions alone, as before.

SettingsActionRow renders a row with a title, description and an action node; it is not a form field.

Generated from the TypeScript declarations of packages/react/src/patterns/layout/settings-section.

Properties

SettingsActionRow

One action in a settings card: title and consequence on the left, a button on the right. Used for danger-zone items (transfer, archive, delete) where the row is not a form control.

PropTypeDescription
action requiredReactNodeThe button that starts the action, usually opening a ConfirmDialog.
title requiredReactNodeWhat the action does ("Delete workspace").
childrenReactNode
classNamestring
descriptionReactNodeThe consequence, in one or two sentences.
idstring

SettingsRow

One setting: label and description on the left, control on the right.

PropTypeDescription
children requiredReactNodeThe control: Input, Select, Switch, Combobox. It receives the Field ids.
label requiredReactNodeLabel of the setting; becomes the control's label.
classNamestring
descriptionReactNode
disabledboolean
errorReactNodeError message rendered under the control.
idstringPass `id` to match an existing control id.
invalidboolean
requiredboolean

SettingsSection

Titled group of settings in a card, as in Workspace App Settings.

PropTypeDescription
title requiredReactNodeSection title (h2).
actionsReactNodeFooter with Save/Cancel actions.
cancelLabelstringDefault "Discard".
childrenReactNode
classNamestring
descriptionReactNode
dirtybooleanThe draft differs from the saved settings. Shows `dirtyMessage` in the footer (a polite status) and enables Save and Discard. Exposed as `data-dirty`.
dirtyMessageReactNodeStatus text while `dirty`. Default "You have unsaved changes."
headingLevel"h2" | "h3"Heading level if the section is nested under another h2.
idstring
loadingLabelstringAnnounced while loading. Default "Loading settings".
noAccessDescriptionReactNodeExplanation under the `no-access` title, such as who can change them.
noAccessTitleReactNodeTitle of the `no-access` notice. Default "You don’t have access to these settings".
onCancel(() => void)Discards the draft. Shown when `onSave` is given.
onSave(() => void)Renders the standard footer: a status line, Discard and Save. The product owns the draft; Save calls this, Discard calls `onCancel`. Save and Discard are disabled while the section is not `dirty`.
saveLabelstringDefault "Save changes".
savingbooleanSave is in flight: Save shows a spinner and Discard is disabled.
state"loading" | "ready" | "no-access"`loading` shows a skeleton instead of the rows (aria-busy); `no-access` replaces the rows and footer with a locked notice. Default `ready`. Exposed as `data-state`.
stickyFooterbooleanKeep the footer stuck to the bottom of the scroll area while `dirty`.
tone"danger" | "default"`danger` for destructive zones (delete workspace, transfer ownership).

Variants

PropValuesDescription
tone'default''danger'danger uses danger border and title colours. Exposed as data-tone.
headingLevel'h2''h3'Heading element for the title. Default h2.
stickyFooter'false''true'While dirty, the footer sticks to the bottom of the scroll area.

States

StateExposed asBehaviour
Dirtydata-dirtyEnables Save and Discard, tints the footer and puts dirtyMessage in the status line.
Savingaria-busySave shows a spinner and ignores clicks; Discard is disabled.
Loadingdata-state="loading"A skeleton replaces the rows; the footer is hidden; the section is busy.
No accessdata-state="no-access"A locked notice replaces the rows and the footer.
Row erroraria-invalid on the controlerror renders a polite live message under the control and marks it invalid.
Row required / disabled–Passed to the control through Field.

Interaction behaviour

  • Save calls onSave; Discard calls onCancel. Both are disabled while the section is not dirty.
  • The status line announces "You have unsaved changes." when the section becomes dirty and clears after saving.
  • Rows have no behaviour of their own beyond Field wiring; controls behave as usual.

Keyboard support

KeysAction
TabMoves through the row controls, then Discard and Save.
Enter / SpaceOn Save or Discard, activates it.

Disabled Save and Discard are skipped by Tab until the section is dirty.

Accessibility

Role
region (section with aria-labelledby)
Accessible name
The section title.
  • Each row label is the control’s accessible name; the description is its accessible description.
  • Row errors are announced through aria-live="polite".
  • The dirty message is a role="status" line, so it is announced without moving focus.
  • Loading and saving set aria-busy on the section.

Responsive behaviour

  • From 1024px the section header sits beside the card (18rem column); below, it stacks above.
  • From 768px each row puts its text and control side by side (control column up to 20rem); below, they stack.
  • The footer wraps: the status line takes its own line on narrow screens and the buttons stay together.

Theme support

  • Card, borders, footer, dirty tint and danger colours use Monarch semantic tokens, so every theme applies.

Do and don't

Do

  • Pass exactly one control per SettingsRow.
  • Derive dirty from the draft and the saved value, so Discard and Save stay honest.
  • Use tone="danger" with SettingsActionRow for irreversible actions, each behind a ConfirmDialog.

Don't

  • Do not wrap the control in another Field.
  • Do not mix destructive and ordinary settings in one section.
  • Do not add a second Save button inside the rows.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsButton, EmptyState, Field, LoadingState

Testing coverage

  • Section is a region named by its title; rows wire label, description and required to the control; an error marks it invalid.
  • Danger tone is exposed as data-tone.
  • The save footer stays disabled until dirty, announces unsaved changes, discards and saves (keyboard).
  • Saving shows a busy Save and blocks Discard; custom labels, sticky footer and extra actions.
  • Loading and no-access states replace the rows and the footer.
  • SettingsActionRow pairs a consequence with its button.
  • Renders on the server.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/settings-section; Cross-browser axe: /components/settings-section

Tests: packages/react/src/patterns/layout/settings-section