SettingsPanel
ImplementedA settings surface's full anatomy: optional panel title, a stack of titled concerns (rendered through FormSection) and an optional actions footer. Chrome-less composition extracted from the 11 dashboard/portal sites that hand-rolled it.
src/components/molecules/SettingsPanel/SettingsPanel.tsxA settings surface’s full anatomy: an optional panel title, a stack of titled concerns (each rendered through FormSection, so the sibling divider rhythm is automatic), and an optional actions footer. Chrome-less by design — carded looks come from the consumer’s className.
SettingsPanel
Molecule — Form family
Default panel (div, 24px rhythm): three concerns stacked through FormSection. This is the composition the dashboard hand-rolled around FormSection in 11 places.
Profile
Preferences
Applies to every device you sign in from.
Notifications
This component supports light and dark mode.
WithTitleAndFooter
Molecule — Form family
Panel title + footer slot: the account-settings shape. The footer renders after the sections and only contributes the vertical gap — pass a styled action row.
Account settings
Profile
Preferences
Applies to every device you sign in from.
Notifications
This component supports light and dark mode.
AsFieldset
Molecule — Form family
as="fieldset" + disabled: the EventForm save-lock. Toggling disabled disables every contained field without touching each one.
This component supports light and dark mode.
Props
titlestringOptional panel heading above the section stack; wired as aria-labelledby.
sections*SettingsPanelSection[]The panel’s concerns — each {title, description?, content, as?, className?} renders as a FormSection; null-content sections are skipped.
footerReact.ReactNodeActions after the section stack. Layout-neutral — bring your own container.
as"div" | "section" | "fieldset"divRoot element — "fieldset" preserves the dashboard’s disable-all semantics.
disabledbooleanForwarded to the root when as="fieldset".
gap"sm" | "md" | "lg" | "none"mdVertical rhythm: md 24px (.um-form), sm 16px (.um-tabpanel), lg 32px (portal), none for margin-managed containers.
classNamestringExtra classes on the root (e.g. um-tabpanel for the carded look).
idstringRoot id — the panel title is wired as aria-labelledby.
Props
SettingsPanel
titlestring—Optional panel heading above the section stack; wired as aria-labelledby
sectionsSettingsPanelSection[]—The panel's concerns — each {title, description?, content, as?, className?} renders as a FormSection; null-content sections are skipped. Named SettingsPanelSection so it cannot collide with the SettingsLayout template's SettingsSection
footerReact.ReactNode—Actions after the section stack (Save/Cancel row). Layout-neutral — bring your own container
as'div' | 'section' | 'fieldset''div'Root element — 'fieldset' preserves the dashboard's disable-all semantics
disabledboolean—Forwarded to the root when as='fieldset' — disables every contained field
gap'sm' | 'md' | 'lg' | 'none''md'Vertical rhythm between title/sections/footer — md 24px (.um-form), sm 16px (.um-tabpanel), lg 32px (portal), none for margin-managed containers
classNamestring—Extra classes on the root (e.g. um-tabpanel for the carded look)
idstring—Root id — the panel title is wired as aria-labelledby
<SettingsPanel />Accessibility
Type
1 ruleARIA
1 ruleUsage
Import
import { SettingsPanel } from "@epasslive/design-system"Example 1
<SettingsPanel as="fieldset" disabled={saving} className="um-tabpanel" gap="sm" sections={[{title: t("sections.general"), content: <ResponsiveFieldGroup>…</ResponsiveFieldGroup>}, …]} />Example 2
<SettingsPanel gap="md" sections={sections} footer={<div className="um-form-actions"><Button>Save</Button></div>} />Anti-patterns
— Do not wrap it in a second sectioning container for spacing — the panel is the container
— Do not hand-roll the action row inside a section's content — use the footer slot
— Do not add card chrome here — pass the carded className (um-tabpanel today) so the dashboard look stays caller-owned