Build / Molecules
SettingRow
One preference: what it is called, what it does, and the control that changes it.
Settings screens are mostly this row repeated, so it is a component rather
than a layout each page reinvents. Consecutive rows draw their own divider,
which means a group is just rows inside a Card — no wrapper element and
no separator to remember.
Passing checked renders the switch and the label together, so the
title is the switch's accessible name and its <label>: the words are part
of the hit target, and assistive tech hears the setting once rather than
twice. Reach for control for everything that is not on/off.
import { SettingRow } from '@meetcard/deck'Example
<Card> <SettingRow title="New connection" description="When someone adds you." checked={alerts} onCheckedChange={setAlerts} /> <SettingRow title="Export my data" description="Cards and notes as JSON." control={<Button variant="secondary">Export</Button>} /> </Card>Stories
The default shape of a preference: a switch whose label is the row's own title, so the words are part of the hit target.
Source
const Toggle = () => {
const [on, setOn] = useState(true)
return (
<Card style={{ maxWidth: 560 }}>
<SettingRow
title="New connection"
description="When someone adds you to their connections."
checked={on}
onCheckedChange={setOn} />
</Card>
);
};Off.
Source
const Off = () => <Card style={{ maxWidth: 560 }}>
<SettingRow
title="New connection"
description="When someone adds you to their connections."
checked={false} />
</Card>;Not every setting is on or off. control takes whatever changes it.
Source
const WithAction = () => <Card style={{ maxWidth: 560 }}>
<SettingRow
title="Export my data"
description="Download a copy of your cards, connections, and notes as JSON."
control={(<Button variant="secondary" size="sm">Export
</Button>)} />
</Card>;A read-only row: the state is reported, and changing it happens elsewhere.
Source
const WithStatus = () => <Card style={{ maxWidth: 560 }}>
<SettingRow
title="Browser push"
description="Push is blocked for this site and can't be reset from here."
control={<Badge tone="error">Blocked</Badge>} />
</Card>;A setting that reveals a follow-on choice. The detail is indented under the row it belongs to, so it reads as part of that setting rather than the next.
Source
const WithDetail = () => {
const [on, setOn] = useState(true)
const [cadence, setCadence] = useState('weekly')
return (
<Card style={{ maxWidth: 560 }}>
<SettingRow
title="Follow-up reminders"
description="Get nudged to follow up with people you've exchanged cards with."
checked={on}
onCheckedChange={setOn} />
{on ? (
<SettingRow
title="Default cadence"
description="How often to surface follow-up reminders."
>
<ChoiceGroup
label="Default cadence"
hideLabel
options={[
{ value: 'daily', label: 'Daily' },
{ value: 'weekly', label: 'Weekly' },
{ value: 'monthly', label: 'Monthly' },
]}
value={cadence}
onChange={setCadence}
/>
</SettingRow>
) : null}
</Card>
);
};Consecutive rows rule themselves off, so a group needs no wrapper.
Source
const Group = function Render() {
const [state, setState] = useState({ connection: true, lead: false, sync: true })
const set = (key: keyof typeof state) => (next: boolean) =>
setState((all) => ({ ...all, [key]: next }))
return (
<Card style={{ maxWidth: 560 }}>
<SettingRow
title="New connection"
description="When someone adds you to their connections."
checked={state.connection}
onCheckedChange={set('connection')}
/>
<SettingRow
title="New lead"
description="When a connection is flagged as a potential lead."
checked={state.lead}
onCheckedChange={set('lead')}
/>
<SettingRow
title="Sync failure"
description="When a CRM sync fails and needs attention."
checked={state.sync}
onCheckedChange={set('sync')}
/>
</Card>
)
};Disabled — a setting that exists but cannot be changed from here.
Source
const Disabled = () => <Card style={{ maxWidth: 560 }}>
<SettingRow
title="New connection"
description="When someone adds you to their connections."
checked={false}
disabled />
</Card>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
titlerequired | ReactNode | — | What the setting is called. Becomes the switch's label in toggle form. |
checked | boolean | — | Present for a toggle row: renders a Switch at the row's end whose
label is this row's own title, so tapping the words works too. |
children | ReactNode | — | Content below the row, indented under it — the follow-on choice a setting reveals, or the detail it needs to explain itself. |
control | ReactNode | — | The row's end slot when it is not a toggle — a Button, a Badge, a
Select. Ignored when checked is given; a row has one control. |
description | ReactNode | — | What turning it on actually does. |
disabled | boolean | — | — |
onCheckedChange | (checked: boolean) => void | — | — |
Also accepts everything from Omit, so id, data-* and event handlers pass straight through.