@nadicodeai/ui 12.1.125 → 12.1.126
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/account-menu.d.ts +5 -4
- package/dist/components/account-menu.d.ts.map +1 -1
- package/dist/components/account-menu.js +4 -4
- package/dist/components/action-menu.d.ts +1 -1
- package/dist/components/action-menu.js +3 -3
- package/dist/components/agent-list-row.d.ts +2 -2
- package/dist/components/agent-list-row.d.ts.map +1 -1
- package/dist/components/agent-list-row.js +2 -2
- package/dist/components/page-header.d.ts +4 -4
- package/dist/components/page-header.js +4 -4
- package/dist/components/reading-column.d.ts +5 -4
- package/dist/components/reading-column.d.ts.map +1 -1
- package/dist/components/reading-column.js +5 -4
- package/dist/components/record-identity.d.ts +3 -2
- package/dist/components/record-identity.d.ts.map +1 -1
- package/dist/components/record-identity.js +1 -1
- package/dist/components/record-list.d.ts +4 -4
- package/dist/components/record-list.d.ts.map +1 -1
- package/dist/components/record-list.js +1 -1
- package/dist/components/settings-page.d.ts +5 -8
- package/dist/components/settings-page.d.ts.map +1 -1
- package/dist/components/settings-page.js +6 -7
- package/dist/components/settings-section.d.ts +6 -24
- package/dist/components/settings-section.d.ts.map +1 -1
- package/dist/components/settings-section.js +5 -5
- package/dist/components/switch.d.ts +4 -5
- package/dist/components/switch.d.ts.map +1 -1
- package/dist/components/switch.js +4 -5
- package/dist/components/theme-mode-switcher.d.ts +1 -1
- package/dist/components/theme-mode-switcher.js +1 -1
- package/dist/internal/record-menu-items.d.ts.map +1 -1
- package/dist/internal/record-menu-items.js +1 -1
- package/dist/showcase/specimens/compositions.js +1 -1
- package/dist/showcase/usage.generated.d.ts.map +1 -1
- package/dist/showcase/usage.generated.js +17 -17
- package/dist/standalone/index.js +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type SettingsSave } from "../internal/settings-save.js";
|
|
3
3
|
import { Field } from "./field.js";
|
|
4
|
-
import { Switch } from "./switch.js";
|
|
5
4
|
export type { SettingsSave, SettingsSaveFailure } from "../internal/settings-save.js";
|
|
6
5
|
export type SettingsFieldControlProps = {
|
|
7
6
|
id: string;
|
|
@@ -12,7 +11,6 @@ export type SettingsFieldControlProps = {
|
|
|
12
11
|
required?: boolean;
|
|
13
12
|
};
|
|
14
13
|
export type SettingsSectionTone = "error" | "warning";
|
|
15
|
-
export type SettingsHeaderSwitch = Pick<React.ComponentProps<typeof Switch>, "checked" | "defaultChecked" | "onCheckedChange" | "disabled" | "name">;
|
|
16
14
|
type SettingsInfo = {
|
|
17
15
|
/** Names the (i) button: "About build command". */
|
|
18
16
|
label: string;
|
|
@@ -35,7 +33,6 @@ type SavingSection = Titled & {
|
|
|
35
33
|
tone?: never;
|
|
36
34
|
lockedReason?: never;
|
|
37
35
|
unlock?: never;
|
|
38
|
-
headerSwitch?: never;
|
|
39
36
|
/** A control on the section's own header: an avatar's upload. */
|
|
40
37
|
actions?: React.ReactNode;
|
|
41
38
|
};
|
|
@@ -49,22 +46,9 @@ type FooterSection = Titled & {
|
|
|
49
46
|
tone?: SettingsSectionTone;
|
|
50
47
|
lockedReason?: never;
|
|
51
48
|
unlock?: never;
|
|
52
|
-
|
|
53
|
-
/** A button or a badge on the section's own header. A switch is `headerSwitch`. */
|
|
49
|
+
/** A button or a badge on the section's own header. */
|
|
54
50
|
actions?: React.ReactNode;
|
|
55
51
|
};
|
|
56
|
-
type SwitchedSection = Titled & {
|
|
57
|
-
save?: never;
|
|
58
|
-
footer?: never;
|
|
59
|
-
/** What is specific to this card, on the footer's left: a limit, what a value is for, a plan it needs, a link. Absent: nothing. */
|
|
60
|
-
footerHint?: React.ReactNode;
|
|
61
|
-
tone?: never;
|
|
62
|
-
lockedReason?: never;
|
|
63
|
-
unlock?: never;
|
|
64
|
-
actions?: never;
|
|
65
|
-
/** The switch that is the whole card, at the title's right, named by the title; it applies at once. */
|
|
66
|
-
headerSwitch: SettingsHeaderSwitch;
|
|
67
|
-
};
|
|
68
52
|
type LockedSection = Titled & {
|
|
69
53
|
save?: never;
|
|
70
54
|
footer?: never;
|
|
@@ -74,7 +58,6 @@ type LockedSection = Titled & {
|
|
|
74
58
|
lockedReason: string | React.ReactElement;
|
|
75
59
|
/** The way to get access: an Upgrade button, a Contact sales link. Left out when nothing the person does unlocks it. */
|
|
76
60
|
unlock?: React.ReactNode;
|
|
77
|
-
headerSwitch?: never;
|
|
78
61
|
actions?: never;
|
|
79
62
|
};
|
|
80
63
|
type UntitledSection = {
|
|
@@ -88,10 +71,9 @@ type UntitledSection = {
|
|
|
88
71
|
tone?: never;
|
|
89
72
|
lockedReason?: never;
|
|
90
73
|
unlock?: never;
|
|
91
|
-
headerSwitch?: never;
|
|
92
74
|
actions?: never;
|
|
93
75
|
};
|
|
94
|
-
export type SettingsSectionProps = SettingsSectionBase & (SavingSection | FooterSection |
|
|
76
|
+
export type SettingsSectionProps = SettingsSectionBase & (SavingSection | FooterSection | LockedSection | UntitledSection);
|
|
95
77
|
export type SavingSettingsSectionProps = SettingsSectionBase & SavingSection;
|
|
96
78
|
/**
|
|
97
79
|
* A card of one setting that says what changing it does and ends in a footer.
|
|
@@ -99,11 +81,11 @@ export type SavingSettingsSectionProps = SettingsSectionBase & SavingSection;
|
|
|
99
81
|
* @family Compositions
|
|
100
82
|
* @when One setting a person changes on a SettingsPage or as a section of an ObjectPage: Personal details, Company identity, Notifications. A typed value takes `save`; a switch or a short reversible choice applies at once through its own control's change, with no Save. Also a plan-gated feature, a delete card, one read-only value (an ID in a read-only mono InputGroup with its copy button, no Save), and a paid quantity (its stepper field, the price as the description, a two-fact summary under it).
|
|
101
83
|
* @instead Read-only facts of an object are a SettingsFactsSection; a table or a list of records is a TableCard; Theme and Language live in the account menu; a record's edit in a list is a RowEditForm.
|
|
102
|
-
* @behaviour "Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card: a single SettingsField, the description its only help. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. One more shape exists, declared by a part and never by classes at the call site. A card of several settings holds SettingsRows, label left and control right, only when every row is a switch or every row is a select. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours:
|
|
84
|
+
* @behaviour "Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card: a single SettingsField, the description its only help. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. One more shape exists, declared by a part and never by classes at the call site. A card of several settings holds SettingsRows, label left and control right, only when every row is a switch or every row is a select. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours: a switch never applies on change; it is a row of the card, a draft until the footer's Save, as Vercel's settings cards are. A switch blocked from above is shown off and disabled, the footer saying why and never a tooltip. Required: the title and the description. The header's actions stand at the title's right; on a card narrower than 28 rem they keep the title's row and the description runs under both, as Vercel's cards read on a phone. The footer's left line (`footerHint`, `save.hint`) is optional and specific to the card, never a generic line repeated on every card: a limit, what a value is for, a plan it needs, a link; absent, it is nothing, and a card with neither a line nor a button has no footer. Ours: `save`: a typed value, one card with its own Save in the footer, always visible. The section owns the changed state: Save stays disabled until a field differs from what is saved and says `pendingLabel` while saving with Cancel beside it; a completed save calls `saved`, and the product raises one toast from its catalogue ("Completion toasts follow `{Noun} {past-participle}`: `Blob deleted`, `Domain added`, `Environment variable saved`. Never include `successfully`; the action name implies it." https://vercel.com/geist/toast), and Save returns to disabled, with no saved state in the footer. Ours: the section never raises a toast itself. A failed save keeps every value and shows under its field, or above the footer when no field owns it. `tone` marks a state with a command (a failed payment, a trial ending): a 1px border and a footer tinted in the hue with its line in it, the line saying the condition in words; title, line, fields and buttons stay neutral, an outline secondary then a default primary. A button takes the hue of its command, not of the card: `destructive` (red) for a permanent loss (Delete), `warning` (amber) for a disruptive command that can be undone (Pause, Archive). A delete card is `tone="error"` with the object's summary as its body; an archive card is neutral with a `warning` button in the footer. "Use a Note for inline contextual feedback next to the field, card, or section it describes: a region change warning above a region picker, a rate-limit notice next to a usage gauge." (https://vercel.com/geist/note) "One Note per concept." (https://vercel.com/geist/note) Ours: a message about the fields inside is a MessageNotice in the body, the card staying neutral. "Disabled checkboxes still need a Tooltip naming the reason; a greyed box with no explanation reads as a bug." (https://vercel.com/geist/checkbox) Ours: `lockedReason` (Geist's disabled wall) is the reason, written as the footer's line, not in a tooltip; it keeps the fields in view, inert with muted text, under a not-allowed cursor, the title and line unchanged; `unlock` is the way to get access, left out when nothing the person does unlocks it. A locked section has no `save` and no header `actions`.
|
|
103
85
|
* @words "Helper text is sentence case, one sentence with a trailing period, on a sibling element wired through `aria-describedby`." (https://vercel.com/geist/input) Ours: the description is that helper text for the whole card, and no field has its own. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: a header-switch card's description says what turning it on does. The title names the values ("Personal details"); the description says what changing them does, in one line; the footer line is a Learn more link, a limit ("Use 32 characters at most."), what stays unaffected, or what is blocked and why ("Ask an owner to change this."); Save and Saving come from the product's catalogue, and so does the toast a `saved` callback raises; a delete card's description opens "Permanently delete".
|
|
104
86
|
* @access "Provide an accessible name through `children`, `aria-label`, or `aria-labelledby`." (https://vercel.com/geist/toggle) Ours: the title is the section's h2 and names the form that saves it, and the header switch. The footer's line is one status, announced politely; a failure under a field marks the control invalid and describes it. Locked fields are inert, so the section's title, description and reason are what a screen reader meets.
|
|
105
87
|
*/
|
|
106
|
-
declare function SettingsSection({ title, description, actions,
|
|
88
|
+
declare function SettingsSection({ title, description, actions, tone, children, className, save, footer, footerHint, lockedReason, unlock, ...props }: SettingsSectionProps): React.JSX.Element;
|
|
107
89
|
type SettingsFieldBase = Omit<React.ComponentProps<typeof Field>, "children" | "orientation"> & {
|
|
108
90
|
id: string;
|
|
109
91
|
label: NonNullable<React.ReactNode>;
|
|
@@ -183,8 +165,8 @@ type SettingsRowsProps = {
|
|
|
183
165
|
*
|
|
184
166
|
* @family Inputs
|
|
185
167
|
* @when A card of settings of one kind, all switches or all selects, that the card's one description covers: a project's Git switches, a team's notification channels, a set of defaults.
|
|
186
|
-
* @instead One setting is a SettingsField. Fields of different kinds, text boxes among them, are one card each.
|
|
187
|
-
* @behaviour Declares `kind` once; every row is that kind, so a card cannot mix a switch with a select. Required on every row: the label and its `info` (an (i) with its sentence in a tooltip and on the control); no help line sits under a row. A switch row shows its state word beside the switch (`stateLabels`) and is controlled, so the word always follows the switch. Rows stand 16 px apart; below 28rem a row stacks its control under its label. "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) Ours: a switch
|
|
168
|
+
* @instead One setting is a SettingsField. Fields of different kinds, text boxes among them, are one card each.
|
|
169
|
+
* @behaviour Declares `kind` once; every row is that kind, so a card cannot mix a switch with a select. Required on every row: the label and its `info` (an (i) with its sentence in a tooltip and on the control); no help line sits under a row. A switch row shows its state word beside the switch (`stateLabels`) and is controlled, so the word always follows the switch. Rows stand 16 px apart; below 28rem a row stacks its control under its label. "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) Ours: a switch is a draft the card's Save applies.
|
|
188
170
|
* @words "Title Case noun phrase, 1–4 words, naming what is true when ON: `Password Protection`, not `Enable Password Protection`." (https://vercel.com/geist/toggle) Ours: sentence case, a noun for what it controls ("Pull request comments"), the same for a switch as for a select. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: that sentence is `info.text`. "Options are Title Case when short and match canonical branding (`Next.js`, not `NextJS`); keep the same register across the list." (https://vercel.com/geist/select) Ours: a select's options are sentence case.
|
|
189
171
|
* @access Each row's control is named by the row's label and described by its info and its error. The info button has its own name and opens on focus.
|
|
190
172
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-section.d.ts","sourceRoot":"","sources":["../../src/components/settings-section.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,EACL,KAAK,YAAY,EAGlB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,KAAK,EAAoD,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"settings-section.d.ts","sourceRoot":"","sources":["../../src/components/settings-section.tsx"],"names":[],"mappings":"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"}
|
|
@@ -22,15 +22,15 @@ const ACTIONS_ON_TITLE_ROW_WHEN_NARROW = "@max-md/settings-section:[&>[data-slot
|
|
|
22
22
|
* @family Compositions
|
|
23
23
|
* @when One setting a person changes on a SettingsPage or as a section of an ObjectPage: Personal details, Company identity, Notifications. A typed value takes `save`; a switch or a short reversible choice applies at once through its own control's change, with no Save. Also a plan-gated feature, a delete card, one read-only value (an ID in a read-only mono InputGroup with its copy button, no Save), and a paid quantity (its stepper field, the price as the description, a two-fact summary under it).
|
|
24
24
|
* @instead Read-only facts of an object are a SettingsFactsSection; a table or a list of records is a TableCard; Theme and Language live in the account menu; a record's edit in a list is a RowEditForm.
|
|
25
|
-
* @behaviour "Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card: a single SettingsField, the description its only help. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. One more shape exists, declared by a part and never by classes at the call site. A card of several settings holds SettingsRows, label left and control right, only when every row is a switch or every row is a select. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours:
|
|
25
|
+
* @behaviour "Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card: a single SettingsField, the description its only help. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. One more shape exists, declared by a part and never by classes at the call site. A card of several settings holds SettingsRows, label left and control right, only when every row is a switch or every row is a select. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours: a switch never applies on change; it is a row of the card, a draft until the footer's Save, as Vercel's settings cards are. A switch blocked from above is shown off and disabled, the footer saying why and never a tooltip. Required: the title and the description. The header's actions stand at the title's right; on a card narrower than 28 rem they keep the title's row and the description runs under both, as Vercel's cards read on a phone. The footer's left line (`footerHint`, `save.hint`) is optional and specific to the card, never a generic line repeated on every card: a limit, what a value is for, a plan it needs, a link; absent, it is nothing, and a card with neither a line nor a button has no footer. Ours: `save`: a typed value, one card with its own Save in the footer, always visible. The section owns the changed state: Save stays disabled until a field differs from what is saved and says `pendingLabel` while saving with Cancel beside it; a completed save calls `saved`, and the product raises one toast from its catalogue ("Completion toasts follow `{Noun} {past-participle}`: `Blob deleted`, `Domain added`, `Environment variable saved`. Never include `successfully`; the action name implies it." https://vercel.com/geist/toast), and Save returns to disabled, with no saved state in the footer. Ours: the section never raises a toast itself. A failed save keeps every value and shows under its field, or above the footer when no field owns it. `tone` marks a state with a command (a failed payment, a trial ending): a 1px border and a footer tinted in the hue with its line in it, the line saying the condition in words; title, line, fields and buttons stay neutral, an outline secondary then a default primary. A button takes the hue of its command, not of the card: `destructive` (red) for a permanent loss (Delete), `warning` (amber) for a disruptive command that can be undone (Pause, Archive). A delete card is `tone="error"` with the object's summary as its body; an archive card is neutral with a `warning` button in the footer. "Use a Note for inline contextual feedback next to the field, card, or section it describes: a region change warning above a region picker, a rate-limit notice next to a usage gauge." (https://vercel.com/geist/note) "One Note per concept." (https://vercel.com/geist/note) Ours: a message about the fields inside is a MessageNotice in the body, the card staying neutral. "Disabled checkboxes still need a Tooltip naming the reason; a greyed box with no explanation reads as a bug." (https://vercel.com/geist/checkbox) Ours: `lockedReason` (Geist's disabled wall) is the reason, written as the footer's line, not in a tooltip; it keeps the fields in view, inert with muted text, under a not-allowed cursor, the title and line unchanged; `unlock` is the way to get access, left out when nothing the person does unlocks it. A locked section has no `save` and no header `actions`.
|
|
26
26
|
* @words "Helper text is sentence case, one sentence with a trailing period, on a sibling element wired through `aria-describedby`." (https://vercel.com/geist/input) Ours: the description is that helper text for the whole card, and no field has its own. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: a header-switch card's description says what turning it on does. The title names the values ("Personal details"); the description says what changing them does, in one line; the footer line is a Learn more link, a limit ("Use 32 characters at most."), what stays unaffected, or what is blocked and why ("Ask an owner to change this."); Save and Saving come from the product's catalogue, and so does the toast a `saved` callback raises; a delete card's description opens "Permanently delete".
|
|
27
27
|
* @access "Provide an accessible name through `children`, `aria-label`, or `aria-labelledby`." (https://vercel.com/geist/toggle) Ours: the title is the section's h2 and names the form that saves it, and the header switch. The footer's line is one status, announced politely; a failure under a field marks the control invalid and describes it. Locked fields are inert, so the section's title, description and reason are what a screen reader meets.
|
|
28
28
|
*/
|
|
29
|
-
function SettingsSection({ title, description, actions,
|
|
29
|
+
function SettingsSection({ title, description, actions, tone, children, className, save, footer, footerHint, lockedReason, unlock, ...props }) {
|
|
30
30
|
const titleId = React.useId();
|
|
31
31
|
const locked = lockedReason !== undefined;
|
|
32
32
|
const fields = children != null ? (_jsx(CardContent, { children: _jsx(FieldGroup, { "data-slot": "settings-section-fields", inert: locked, className: cn(locked && "**:text-muted-foreground"), children: children }) })) : null;
|
|
33
|
-
return (_jsxs(Card, { render: _jsx("section", { ...props, "data-slot": "settings-section", "data-tone": tone, "data-locked": locked || undefined }), className: cn("@container/settings-section min-w-0", tone && toneClassNames[tone], locked && "cursor-not-allowed *:data-[slot=card-footer]:cursor-default", className), children: [_jsxs(CardHeader, { className: cn(ACTIONS_ON_TITLE_ROW_WHEN_NARROW, !title && "has-data-[slot=card-description]:grid-rows-none"), children: [title ? (_jsx(CardTitle, { children: _jsx("h2", { id: titleId, children: title }) })) : null, _jsx(CardDescription, { children: description }), actions ? _jsx(CardAction, { children: actions }) : null
|
|
33
|
+
return (_jsxs(Card, { render: _jsx("section", { ...props, "data-slot": "settings-section", "data-tone": tone, "data-locked": locked || undefined }), className: cn("@container/settings-section min-w-0", tone && toneClassNames[tone], locked && "cursor-not-allowed *:data-[slot=card-footer]:cursor-default", className), children: [_jsxs(CardHeader, { className: cn(ACTIONS_ON_TITLE_ROW_WHEN_NARROW, !title && "has-data-[slot=card-description]:grid-rows-none"), children: [title ? (_jsx(CardTitle, { children: _jsx("h2", { id: titleId, children: title }) })) : null, _jsx(CardDescription, { children: description }), actions ? _jsx(CardAction, { children: actions }) : null] }), save ? (_jsx(SettingsSaveForm, { save: save, titleId: titleId, children: fields })) : (_jsxs(_Fragment, { children: [fields, _jsx(FooterStrip, { hint: locked ? lockedReason : footerHint, hintSlot: "settings-section-hint", children: locked ? unlock : footer })] }))] }));
|
|
34
34
|
}
|
|
35
35
|
function controlPropsOf({ id, info, error, invalid, disabled, required, }) {
|
|
36
36
|
return {
|
|
@@ -92,8 +92,8 @@ function SettingsRow({ id, label, info, error, disabled, children, }) {
|
|
|
92
92
|
*
|
|
93
93
|
* @family Inputs
|
|
94
94
|
* @when A card of settings of one kind, all switches or all selects, that the card's one description covers: a project's Git switches, a team's notification channels, a set of defaults.
|
|
95
|
-
* @instead One setting is a SettingsField. Fields of different kinds, text boxes among them, are one card each.
|
|
96
|
-
* @behaviour Declares `kind` once; every row is that kind, so a card cannot mix a switch with a select. Required on every row: the label and its `info` (an (i) with its sentence in a tooltip and on the control); no help line sits under a row. A switch row shows its state word beside the switch (`stateLabels`) and is controlled, so the word always follows the switch. Rows stand 16 px apart; below 28rem a row stacks its control under its label. "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) Ours: a switch
|
|
95
|
+
* @instead One setting is a SettingsField. Fields of different kinds, text boxes among them, are one card each.
|
|
96
|
+
* @behaviour Declares `kind` once; every row is that kind, so a card cannot mix a switch with a select. Required on every row: the label and its `info` (an (i) with its sentence in a tooltip and on the control); no help line sits under a row. A switch row shows its state word beside the switch (`stateLabels`) and is controlled, so the word always follows the switch. Rows stand 16 px apart; below 28rem a row stacks its control under its label. "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) Ours: a switch is a draft the card's Save applies.
|
|
97
97
|
* @words "Title Case noun phrase, 1–4 words, naming what is true when ON: `Password Protection`, not `Enable Password Protection`." (https://vercel.com/geist/toggle) Ours: sentence case, a noun for what it controls ("Pull request comments"), the same for a switch as for a select. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: that sentence is `info.text`. "Options are Title Case when short and match canonical branding (`Next.js`, not `NextJS`); keep the same register across the list." (https://vercel.com/geist/select) Ours: a select's options are sentence case.
|
|
98
98
|
* @access Each row's control is named by the row's label and described by its info and its error. The info button has its own name and opens on focus.
|
|
99
99
|
*/
|
|
@@ -24,11 +24,10 @@ import { type ControlName } from "../internal/control-name.js";
|
|
|
24
24
|
* and a switch with none of them does not compile. A bare `Switch` is the 28 by 14 px track with an
|
|
25
25
|
* 11 px thumb; `md` is 36 by 20 px and `lg` 40 by 24 px. Colour: off is the input grey, on is
|
|
26
26
|
* ink, and a disabled switch turns grey, on or off, with a greyer thumb and no shadow. Space flips
|
|
27
|
-
* it. On a settings page it
|
|
28
|
-
* places: in the card's body under its label, its state word after
|
|
29
|
-
* `control="switch"`);
|
|
30
|
-
*
|
|
31
|
-
* its left. A failed change leaves the switch showing the true state.
|
|
27
|
+
* it. On a settings page it is a draft its card's footer Save applies, and the product's toast
|
|
28
|
+
* confirms the save, in one of two places: in the card's body under its label, its state word after
|
|
29
|
+
* it (`SettingsField`'s `control="switch"`); or at the end of a row of a card of several settings,
|
|
30
|
+
* the label to its left. A failed save keeps the draft and says so under the card.
|
|
32
31
|
* @words "The label is `children`, not a `label` prop. Title Case noun phrase, 1–4 words, naming
|
|
33
32
|
* what is true when ON: `Password Protection`, not `Enable Password Protection`."
|
|
34
33
|
* (https://vercel.com/geist/toggle) "Render an optional one-sentence description as a sibling under
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../src/components/switch.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D
|
|
1
|
+
{"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../src/components/switch.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,iBAAS,MAAM,CAAC,EACd,SAAS,EACT,IAAW,EACX,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,IAAI,CAAC,KAAK,GAC3B,WAAW,GAAG;IACZ,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B,+BAiBF;AAED,OAAO,EAAE,MAAM,EAAE,CAAC"}
|
|
@@ -25,11 +25,10 @@ import { cn } from "../lib/utils.js";
|
|
|
25
25
|
* and a switch with none of them does not compile. A bare `Switch` is the 28 by 14 px track with an
|
|
26
26
|
* 11 px thumb; `md` is 36 by 20 px and `lg` 40 by 24 px. Colour: off is the input grey, on is
|
|
27
27
|
* ink, and a disabled switch turns grey, on or off, with a greyer thumb and no shadow. Space flips
|
|
28
|
-
* it. On a settings page it
|
|
29
|
-
* places: in the card's body under its label, its state word after
|
|
30
|
-
* `control="switch"`);
|
|
31
|
-
*
|
|
32
|
-
* its left. A failed change leaves the switch showing the true state.
|
|
28
|
+
* it. On a settings page it is a draft its card's footer Save applies, and the product's toast
|
|
29
|
+
* confirms the save, in one of two places: in the card's body under its label, its state word after
|
|
30
|
+
* it (`SettingsField`'s `control="switch"`); or at the end of a row of a card of several settings,
|
|
31
|
+
* the label to its left. A failed save keeps the draft and says so under the card.
|
|
33
32
|
* @words "The label is `children`, not a `label` prop. Title Case noun phrase, 1–4 words, naming
|
|
34
33
|
* what is true when ON: `Password Protection`, not `Enable Password Protection`."
|
|
35
34
|
* (https://vercel.com/geist/toggle) "Render an optional one-sentence description as a sibling under
|
|
@@ -25,7 +25,7 @@ export type ThemeModeSwitcherProps = Omit<React.ComponentProps<typeof RadioGroup
|
|
|
25
25
|
* @family Choices
|
|
26
26
|
* @when An Appearance setting on a settings page, where the choice has room to show its names.
|
|
27
27
|
* @instead Inside a menu, the same choice is ThemeModeMenu; a choice that is not the appearance is a RadioGroup or a ToggleGroup.
|
|
28
|
-
* @behaviour Exclusive: one mode is selected
|
|
28
|
+
* @behaviour Exclusive: one mode is selected; on a settings card the choice is a draft the card's Save applies. The order is System, Light, Dark everywhere.
|
|
29
29
|
* @words The setting's name, then each mode's name from the product's catalogue: "System", "Light", "Dark".
|
|
30
30
|
* @access A radio group named by `aria-label`; each mode is a radio with its name, moved through with the arrow keys.
|
|
31
31
|
*/
|
|
@@ -19,7 +19,7 @@ const themeModeIcons = {
|
|
|
19
19
|
* @family Choices
|
|
20
20
|
* @when An Appearance setting on a settings page, where the choice has room to show its names.
|
|
21
21
|
* @instead Inside a menu, the same choice is ThemeModeMenu; a choice that is not the appearance is a RadioGroup or a ToggleGroup.
|
|
22
|
-
* @behaviour Exclusive: one mode is selected
|
|
22
|
+
* @behaviour Exclusive: one mode is selected; on a settings card the choice is a draft the card's Save applies. The order is System, Light, Dark everywhere.
|
|
23
23
|
* @words The setting's name, then each mode's name from the product's catalogue: "System", "Light", "Dark".
|
|
24
24
|
* @access A radio group named by `aria-label`; each mode is a radio with its name, moved through with the arrow keys.
|
|
25
25
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-menu-items.d.ts","sourceRoot":"","sources":["../../src/internal/record-menu-items.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAQxE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEhF,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB;;;;;;;;EAMvD;AAYD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,OAAe,EACf,KAAK,GACN,EAAE;IACD,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,+BAuBA;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,GAAG,SAAS,CAKxF;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,+
|
|
1
|
+
{"version":3,"file":"record-menu-items.d.ts","sourceRoot":"","sources":["../../src/internal/record-menu-items.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAQxE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEhF,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB;;;;;;;;EAMvD;AAYD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,OAAe,EACf,KAAK,GACN,EAAE;IACD,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,+BAuBA;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,GAAG,SAAS,CAKxF;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,+BAiB9E"}
|
|
@@ -31,5 +31,5 @@ export function soleMenuButton(commands) {
|
|
|
31
31
|
export function RecordMenuItems({ record, commands }) {
|
|
32
32
|
const kept = commands.filter((command) => !command.destructive);
|
|
33
33
|
const destructive = commands.filter((command) => command.destructive);
|
|
34
|
-
return (_jsxs(DropdownMenuGroup, { children: [_jsx(DropdownMenuLabel, { children: record }), kept.map((command) => (_jsx(MenuCommandRow, { command: command }, command.label))), kept.length > 0 && destructive.length > 0 ? _jsx(DropdownMenuSeparator, {}) : null, destructive.map((command) => (_jsx(MenuCommandRow, { command: command }, command.label)))] }));
|
|
34
|
+
return (_jsxs(DropdownMenuGroup, { children: [_jsx(DropdownMenuLabel, { className: "truncate", title: record, children: record }), kept.map((command) => (_jsx(MenuCommandRow, { command: command }, command.label))), kept.length > 0 && destructive.length > 0 ? _jsx(DropdownMenuSeparator, {}) : null, destructive.map((command) => (_jsx(MenuCommandRow, { command: command }, command.label)))] }));
|
|
35
35
|
}
|
|
@@ -378,6 +378,6 @@ export const COMPOSITIONS = {
|
|
|
378
378
|
}), records: MAILBOXES }), _jsx(SelectableRecordListSpecimen, {})] })),
|
|
379
379
|
RecordSheet: RecordSheetSpecimen,
|
|
380
380
|
RowEditForm: RowEditFormSpecimen,
|
|
381
|
-
SettingsPage: () => (_jsx(SettingsPage, {
|
|
381
|
+
SettingsPage: () => (_jsx(SettingsPage, { children: _jsx(SenderSection, {}) })),
|
|
382
382
|
SettingsSection: SettingsSectionSpecimen,
|
|
383
383
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.generated.d.ts","sourceRoot":"","sources":["../../src/showcase/usage.generated.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,SAAS,GACjB,cAAc,GACd,kBAAkB,GAClB,eAAe,GACf,mBAAmB,GACnB,gBAAgB,GAChB,mBAAmB,GACnB,eAAe,GACf,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,cAAc,GACd,YAAY,GACZ,eAAe,GACf,cAAc,GACd,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,QAAQ,GACR,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,gBAAgB,GAChB,aAAa,GACb,UAAU,GACV,gBAAgB,GAChB,kBAAkB,GAClB,OAAO,GACP,OAAO,GACP,YAAY,GACZ,UAAU,GACV,OAAO,GACP,cAAc,GACd,eAAe,GACf,cAAc,GACd,QAAQ,GACR,UAAU,GACV,UAAU,GACV,gBAAgB,GAChB,gBAAgB,GAChB,UAAU,GACV,aAAa,GACb,aAAa,GACb,YAAY,GACZ,iBAAiB,GACjB,kBAAkB,GAClB,QAAQ,GACR,QAAQ,GACR,mBAAmB,GACnB,mBAAmB,GACnB,QAAQ,GACR,aAAa,GACb,cAAc,GACd,UAAU,GACV,SAAS,GACT,UAAU,GACV,QAAQ,GACR,OAAO,GACP,eAAe,GACf,aAAa,GACb,cAAc,GACd,cAAc,GACd,eAAe,GACf,WAAW,GACX,iBAAiB,GACjB,cAAc,GACd,mBAAmB,GACnB,gBAAgB,GAChB,aAAa,GACb,KAAK,GACL,cAAc,GACd,YAAY,GACZ,eAAe,GACf,QAAQ,GACR,oBAAoB,GACpB,eAAe,GACf,SAAS,GACT,cAAc,GACd,aAAa,GACb,cAAc,GACd,oBAAoB,GACpB,iBAAiB,GACjB,MAAM,GACN,SAAS,GACT,kBAAkB,GAClB,WAAW,GACX,MAAM,GACN,SAAS,GACT,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,UAAU,GACV,OAAO,GACP,MAAM,GACN,KAAK,GACL,gBAAgB,GAChB,aAAa,GACb,aAAa,GACb,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,aAAa,GACb,sBAAsB,GACtB,SAAS,GACT,OAAO,GACP,WAAW,GACX,YAAY,GACZ,oBAAoB,GACpB,YAAY,GACZ,OAAO,GACP,YAAY,GACZ,eAAe,GACf,gBAAgB,GAChB,WAAW,GACX,eAAe,GACf,cAAc,GACd,cAAc,GACd,UAAU,GACV,gBAAgB,GAChB,SAAS,GACT,SAAS,GACT,YAAY,GACZ,aAAa,GACb,aAAa,GACb,mBAAmB,GACnB,YAAY,GACZ,aAAa,GACb,aAAa,GACb,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,cAAc,GACd,eAAe,GACf,SAAS,GACT,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,kBAAkB,GAClB,oBAAoB,GACpB,kBAAkB,GAClB,iBAAiB,GACjB,WAAW,GACX,gBAAgB,GAChB,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,SAAS,GACT,YAAY,GACZ,sBAAsB,GACtB,WAAW,GACX,aAAa,GACb,YAAY,GACZ,UAAU,GACV,aAAa,GACb,aAAa,GACb,YAAY,GACZ,WAAW,GACX,eAAe,GACf,eAAe,GACf,OAAO,GACP,eAAe,GACf,qBAAqB,GACrB,YAAY,GACZ,WAAW,GACX,UAAU,CAAC;AAEf,MAAM,MAAM,WAAW,GACnB,cAAc,GACd,SAAS,GACT,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,YAAY,GACZ,MAAM,GACN,UAAU,GACV,OAAO,GACP,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,SAAS,WAAW,EAYhD,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,SAAS,cAAc,
|
|
1
|
+
{"version":3,"file":"usage.generated.d.ts","sourceRoot":"","sources":["../../src/showcase/usage.generated.ts"],"names":[],"mappings":"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"}
|
|
@@ -173,9 +173,9 @@ export const USAGE = [
|
|
|
173
173
|
module: "page-header",
|
|
174
174
|
family: "Compositions",
|
|
175
175
|
summary: "The top of a product page: its title, and the page's actions on the title's row.",
|
|
176
|
-
when: "The top of every collection and
|
|
176
|
+
when: "The top of every page, collection, working page and settings page alike: the title at heading size, `PageActions` at the trailing edge of its row.",
|
|
177
177
|
instead: "An object's own header is `ObjectPage`'s. The path above it is the top bar's `Breadcrumb`; filters, search and sort sit in the toolbar below.",
|
|
178
|
-
behaviour: "The title is always there and the actions arrive through the `PageActions` slot, so the page draws its commands once. On a phone the title keeps its row with one action, as Vercel's does; it gives way, and the actions wrap under it, only when they leave it less than 128 px. Under the title
|
|
178
|
+
behaviour: "The title is always there and the actions arrive through the `PageActions` slot, so the page draws its commands once. On a phone the title keeps its row with one action, as Vercel's does; it gives way, and the actions wrap under it, only when they leave it less than 128 px. Under the title sits the `description`: one muted line saying what the page's concept is, on every page that is a destination or a section. It ends in a link to learn more when there is one, and never counts or introduces anything else. The description is neutral; no hue sits in the header.",
|
|
179
179
|
words: "The title is the page's name as the sidebar writes it: *Customers*, *Agents*. The description is one sentence in the product's words, without a full stop when it ends in a link.",
|
|
180
180
|
access: "The title is the page's `h1`; DOM order is visual order.",
|
|
181
181
|
},
|
|
@@ -186,7 +186,7 @@ export const USAGE = [
|
|
|
186
186
|
summary: "Entity rows: objects a person opens or acts on, one line each on one resting card: the identity mark and name over what it is, a few facts, a status only for the exception, when it happened, at most two row commands, and the three dots last.",
|
|
187
187
|
when: "“Use `<Entity>` for a row of descriptive content paired with one or two controls (member rows, integration rows, domain rows).” (https://vercel.com/geist/entity) Ours: the records are things a person opens or acts on and that name themselves: projects, deployments, domains, members, integrations, Agents, Skills, mailboxes. A list inside another page: an Overview's exceptions, an object page's \"Needs attention\". A page whose whole job is the list is a `CollectionPage`, which draws this. Values edited in place (a domain, an environment variable) are the same rows with an `editor`: the row expands into its form. Of the three kinds of list, entity rows and rows edited in place are this; records compared across columns are a `DataTable`.",
|
|
188
188
|
instead: "“For tabular data with sortable columns and shared row shape, use `Table` instead.” (https://vercel.com/geist/entity) “For a static key/value metadata block on a detail page, use `Description`.” (https://vercel.com/geist/entity) Ours: Geist's `Table` is a `DataTable`: this list has no visible headings and at most three facts, so a fourth fact is a table, and anything more than a few facts lives on the record's page. A screen gives `RecordList` the records and what each one says, never its own toolbar, table, rows or menus, and nothing else draws a record row. Its `Description` is `ObjectFacts`. Events in time are an `ActivityFeed`. Reusable things a person adds from a catalogue are a `LibraryShelf`. A descriptive row with prose is an `Item`.",
|
|
189
|
-
behaviour: "One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name`
|
|
189
|
+
behaviour: "One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout=\"line\"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as one muted line joined by a middle dot, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts, the status and the time leave the row for that one line, ending in an ellipsis, never a labelled list. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list (\"Deployment retention is enabled. Learn more\", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message. “Pick the variant by what the user needs: no-results for a filtered list that returned zero rows, blank slate or informational for a resource the user hasn’t created, cleared for completed work, permission for role or tier denials, error for a failed load.” (https://vercel.com/geist/empty-state) Ours: `failed`, `noMatch` and `empty` are those variants, drawn inside the card. “The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`.” (https://vercel.com/geist/entity) Ours: one or two row commands (outline buttons of 32 px) stand before the three dots (36 px), the Dots Menu. “Render the Skeleton variant (`entity-with-skeleton`) during load instead of an empty row, and swap to real content once data resolves.” (https://vercel.com/geist/entity) Ours: three still skeleton rows hold the place, in a region that announces what replaces them; then the first that holds: the failed read, no match, the empty state, the rows.",
|
|
190
190
|
words: '“Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity) Ours: the name is the record\'s own and the labels around it are sentence case, not Title Case. “Keep right-column buttons Verb + Noun (`Remove Member`, `Resend Invite`). Bare verbs like `Remove` or `Confirm` lose context once the row scrolls offscreen.” (https://vercel.com/geist/entity) Ours: every string is the product\'s: `label` names the list, a time\'s `phrase` writes its sentence ("Updated {when}"), `missing` is what a screen reader hears for a record with no time, the menu\'s `label` reads "More for {record}". A row command names its result in sentence case ("Edit domain"). A status is the exception\'s word ("Needs attention"), never "Active" on every row.',
|
|
191
191
|
access: '“Wrap the loading region in `aria-busy="true"` and announce completion with `aria-live="polite"` on the destination container, not the skeleton itself.” (https://vercel.com/geist/skeleton) Ours: the loading rows sit in one `SkeletonRegion` that is busy while they stand and announces what replaces them. A table named by `label`, its column headings read by assistive technology only. The row link carries `open.label`; the three dots carry the menu\'s `label`, and the menu\'s first line names the record. The loading rows are a status named by `loadingLabel`, inside the one live region that announces what replaces them. “For multi-select rows, the leading `Checkbox` carries `aria-label="Select {entity name}"` so the row is selectable without relying on the visual label.” (https://vercel.com/geist/entity) Ours: each box is named by `select.label`, which reads "Select {record}".',
|
|
192
192
|
},
|
|
@@ -219,9 +219,9 @@ export const USAGE = [
|
|
|
219
219
|
summary: "A page of settings: section cards across the page's width under its title, each saving its own values.",
|
|
220
220
|
when: "A place whose job is changing how something behaves: an account's profile, a company's identity, Nadia's notifications.",
|
|
221
221
|
instead: 'One object\'s page with a settings card among its sections is an ObjectPage; "Place it once per app, in the footer or settings, not duplicated across pages." (https://vercel.com/geist/theme-switcher) Ours: Theme and Language, which apply on change, stay in the account menu.',
|
|
222
|
-
behaviour: "One setting per card (the card's shapes are SettingsSection's: one field, a row of short parts of one value, rows of switches or of selects
|
|
223
|
-
words: "
|
|
224
|
-
access: "
|
|
222
|
+
behaviour: "One setting per card (the card's shapes are SettingsSection's: one field, a row of short parts of one value, rows of switches or of selects), the cards stacked 24 apart in the settings column (`ReadingColumn` `settings`, 960 px) under the page's header, which the product's page frame draws with its title and description, as Vercel's settings cards fill their column and stop there, with no heading between them; a field inside keeps its own bound. A page that mixes cards with other content, such as a table or a list of rules, is not a SettingsPage: its sections take a plain h2 above each card. The page draws no header and no save of its own: each SettingsSection saves from its footer. The danger zone closes the page, in rising severity: transfer, then pause, then delete; only delete is `tone=\"error\"`, and only delete and pause take a button in a hue.",
|
|
223
|
+
words: "The sections' titles and lines; a card's title is never the page's h1.",
|
|
224
|
+
access: "Draws no heading of its own: the page frame's header is the page's h1.",
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
name: "SettingsSection",
|
|
@@ -230,7 +230,7 @@ export const USAGE = [
|
|
|
230
230
|
summary: "A card of one setting that says what changing it does and ends in a footer.",
|
|
231
231
|
when: "One setting a person changes on a SettingsPage or as a section of an ObjectPage: Personal details, Company identity, Notifications. A typed value takes `save`; a switch or a short reversible choice applies at once through its own control's change, with no Save. Also a plan-gated feature, a delete card, one read-only value (an ID in a read-only mono InputGroup with its copy button, no Save), and a paid quantity (its stepper field, the price as the description, a two-fact summary under it).",
|
|
232
232
|
instead: "Read-only facts of an object are a SettingsFactsSection; a table or a list of records is a TableCard; Theme and Language live in the account menu; a record's edit in a list is a RowEditForm.",
|
|
233
|
-
behaviour: '"Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card: a single SettingsField, the description its only help. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. One more shape exists, declared by a part and never by classes at the call site. A card of several settings holds SettingsRows, label left and control right, only when every row is a switch or every row is a select. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours:
|
|
233
|
+
behaviour: '"Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card: a single SettingsField, the description its only help. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. One more shape exists, declared by a part and never by classes at the call site. A card of several settings holds SettingsRows, label left and control right, only when every row is a switch or every row is a select. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours: a switch never applies on change; it is a row of the card, a draft until the footer\'s Save, as Vercel\'s settings cards are. A switch blocked from above is shown off and disabled, the footer saying why and never a tooltip. Required: the title and the description. The header\'s actions stand at the title\'s right; on a card narrower than 28 rem they keep the title\'s row and the description runs under both, as Vercel\'s cards read on a phone. The footer\'s left line (`footerHint`, `save.hint`) is optional and specific to the card, never a generic line repeated on every card: a limit, what a value is for, a plan it needs, a link; absent, it is nothing, and a card with neither a line nor a button has no footer. Ours: `save`: a typed value, one card with its own Save in the footer, always visible. The section owns the changed state: Save stays disabled until a field differs from what is saved and says `pendingLabel` while saving with Cancel beside it; a completed save calls `saved`, and the product raises one toast from its catalogue ("Completion toasts follow `{Noun} {past-participle}`: `Blob deleted`, `Domain added`, `Environment variable saved`. Never include `successfully`; the action name implies it." https://vercel.com/geist/toast), and Save returns to disabled, with no saved state in the footer. Ours: the section never raises a toast itself. A failed save keeps every value and shows under its field, or above the footer when no field owns it. `tone` marks a state with a command (a failed payment, a trial ending): a 1px border and a footer tinted in the hue with its line in it, the line saying the condition in words; title, line, fields and buttons stay neutral, an outline secondary then a default primary. A button takes the hue of its command, not of the card: `destructive` (red) for a permanent loss (Delete), `warning` (amber) for a disruptive command that can be undone (Pause, Archive). A delete card is `tone="error"` with the object\'s summary as its body; an archive card is neutral with a `warning` button in the footer. "Use a Note for inline contextual feedback next to the field, card, or section it describes: a region change warning above a region picker, a rate-limit notice next to a usage gauge." (https://vercel.com/geist/note) "One Note per concept." (https://vercel.com/geist/note) Ours: a message about the fields inside is a MessageNotice in the body, the card staying neutral. "Disabled checkboxes still need a Tooltip naming the reason; a greyed box with no explanation reads as a bug." (https://vercel.com/geist/checkbox) Ours: `lockedReason` (Geist\'s disabled wall) is the reason, written as the footer\'s line, not in a tooltip; it keeps the fields in view, inert with muted text, under a not-allowed cursor, the title and line unchanged; `unlock` is the way to get access, left out when nothing the person does unlocks it. A locked section has no `save` and no header `actions`.',
|
|
234
234
|
words: '"Helper text is sentence case, one sentence with a trailing period, on a sibling element wired through `aria-describedby`." (https://vercel.com/geist/input) Ours: the description is that helper text for the whole card, and no field has its own. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: a header-switch card\'s description says what turning it on does. The title names the values ("Personal details"); the description says what changing them does, in one line; the footer line is a Learn more link, a limit ("Use 32 characters at most."), what stays unaffected, or what is blocked and why ("Ask an owner to change this."); Save and Saving come from the product\'s catalogue, and so does the toast a `saved` callback raises; a delete card\'s description opens "Permanently delete".',
|
|
235
235
|
access: "\"Provide an accessible name through `children`, `aria-label`, or `aria-labelledby`.\" (https://vercel.com/geist/toggle) Ours: the title is the section's h2 and names the form that saves it, and the header switch. The footer's line is one status, announced politely; a failure under a field marks the control invalid and describes it. Locked fields are inert, so the section's title, description and reason are what a screen reader meets.",
|
|
236
236
|
},
|
|
@@ -416,8 +416,8 @@ export const USAGE = [
|
|
|
416
416
|
family: "Inputs",
|
|
417
417
|
summary: "Several switches, or several selects, in one card: each row its label on the left and its control on the right.",
|
|
418
418
|
when: "A card of settings of one kind, all switches or all selects, that the card's one description covers: a project's Git switches, a team's notification channels, a set of defaults.",
|
|
419
|
-
instead: "One setting is a SettingsField. Fields of different kinds, text boxes among them, are one card each.
|
|
420
|
-
behaviour: 'Declares `kind` once; every row is that kind, so a card cannot mix a switch with a select. Required on every row: the label and its `info` (an (i) with its sentence in a tooltip and on the control); no help line sits under a row. A switch row shows its state word beside the switch (`stateLabels`) and is controlled, so the word always follows the switch. Rows stand 16 px apart; below 28rem a row stacks its control under its label. "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) Ours: a switch
|
|
419
|
+
instead: "One setting is a SettingsField. Fields of different kinds, text boxes among them, are one card each.",
|
|
420
|
+
behaviour: 'Declares `kind` once; every row is that kind, so a card cannot mix a switch with a select. Required on every row: the label and its `info` (an (i) with its sentence in a tooltip and on the control); no help line sits under a row. A switch row shows its state word beside the switch (`stateLabels`) and is controlled, so the word always follows the switch. Rows stand 16 px apart; below 28rem a row stacks its control under its label. "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) Ours: a switch is a draft the card\'s Save applies.',
|
|
421
421
|
words: '"Title Case noun phrase, 1–4 words, naming what is true when ON: `Password Protection`, not `Enable Password Protection`." (https://vercel.com/geist/toggle) Ours: sentence case, a noun for what it controls ("Pull request comments"), the same for a switch as for a select. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: that sentence is `info.text`. "Options are Title Case when short and match canonical branding (`Next.js`, not `NextJS`); keep the same register across the list." (https://vercel.com/geist/select) Ours: a select\'s options are sentence case.',
|
|
422
422
|
access: "Each row's control is named by the row's label and described by its info and its error. The info button has its own name and opens on focus.",
|
|
423
423
|
},
|
|
@@ -560,7 +560,7 @@ export const USAGE = [
|
|
|
560
560
|
summary: "A setting that is on or off.",
|
|
561
561
|
when: '"Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) Ours: Geist\'s `Toggle` is our `Switch`, inside a settings section.',
|
|
562
562
|
instead: '"Switch to `Checkbox` for a multi-select list, or to `Switch` for 2–3 mutually exclusive views." (https://vercel.com/geist/toggle) Ours: Geist\'s `Switch` is our `SegmentedControl`. Our `Toggle` is a button that stays pressed, and several of them are a `ToggleGroup`. Choosing between named values is a `RadioGroup`.',
|
|
563
|
-
behaviour: '"`checked` is required and the component is controlled. Own the state and update it from `onChange`." (https://vercel.com/geist/toggle) "Persist on change and surface the result in a `useToasts().success(…)` toast (`Password protection enabled`); don’t leave the user wondering whether the flip stuck." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours: `checked` is optional, since a switch also runs uncontrolled with `defaultChecked`; the toast is raised with `showMessageToast`; the reason for a disabled switch is written in the card\'s footer, and it is left off. Always named: `aria-label` or `aria-labelledby`, or an `id` that a `FieldLabel`\'s `htmlFor` points at, and a switch with none of them does not compile. A bare `Switch` is the 28 by 14 px track with an 11 px thumb; `md` is 36 by 20 px and `lg` 40 by 24 px. Colour: off is the input grey, on is ink, and a disabled switch turns grey, on or off, with a greyer thumb and no shadow. Space flips it. On a settings page it
|
|
563
|
+
behaviour: '"`checked` is required and the component is controlled. Own the state and update it from `onChange`." (https://vercel.com/geist/toggle) "Persist on change and surface the result in a `useToasts().success(…)` toast (`Password protection enabled`); don’t leave the user wondering whether the flip stuck." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours: `checked` is optional, since a switch also runs uncontrolled with `defaultChecked`; the toast is raised with `showMessageToast`; the reason for a disabled switch is written in the card\'s footer, and it is left off. Always named: `aria-label` or `aria-labelledby`, or an `id` that a `FieldLabel`\'s `htmlFor` points at, and a switch with none of them does not compile. A bare `Switch` is the 28 by 14 px track with an 11 px thumb; `md` is 36 by 20 px and `lg` 40 by 24 px. Colour: off is the input grey, on is ink, and a disabled switch turns grey, on or off, with a greyer thumb and no shadow. Space flips it. On a settings page it is a draft its card\'s footer Save applies, and the product\'s toast confirms the save, in one of two places: in the card\'s body under its label, its state word after it (`SettingsField`\'s `control="switch"`); or at the end of a row of a card of several settings, the label to its left. A failed save keeps the draft and says so under the card.',
|
|
564
564
|
words: '"The label is `children`, not a `label` prop. Title Case noun phrase, 1–4 words, naming what is true when ON: `Password Protection`, not `Enable Password Protection`." (https://vercel.com/geist/toggle) "Render an optional one-sentence description as a sibling under the label that explains ON only." (https://vercel.com/geist/toggle) "Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) "Keep `labelCasing="title"` (the default) so labels match other Title Case surfaces." (https://vercel.com/geist/toggle) "Use `labelCasing="normal"` only when sentence-case content sits inline next to the toggle." (https://vercel.com/geist/toggle) Ours: the label is 12 px at medium weight, in sentence case: *Approvals*, with the card\'s description in one sentence. The state word is a separate word beside the switch, not its name.',
|
|
565
565
|
access: '"Provide an accessible name through `children`, `aria-label`, or `aria-labelledby`." (https://vercel.com/geist/toggle) "Geist warns in development if all three are missing." (https://vercel.com/geist/toggle) "Set `aria-label` only when the visible label sits elsewhere on the row; otherwise let `children` carry it so sighted and screen-reader copy stay aligned." (https://vercel.com/geist/toggle) Ours: `role="switch"` with `aria-checked`, the name the same in both states; a missing name does not compile.',
|
|
566
566
|
},
|
|
@@ -582,7 +582,7 @@ export const USAGE = [
|
|
|
582
582
|
summary: "The three appearances side by side, each with its glyph and its name.",
|
|
583
583
|
when: "An Appearance setting on a settings page, where the choice has room to show its names.",
|
|
584
584
|
instead: "Inside a menu, the same choice is ThemeModeMenu; a choice that is not the appearance is a RadioGroup or a ToggleGroup.",
|
|
585
|
-
behaviour: "Exclusive: one mode is selected
|
|
585
|
+
behaviour: "Exclusive: one mode is selected; on a settings card the choice is a draft the card's Save applies. The order is System, Light, Dark everywhere.",
|
|
586
586
|
words: 'The setting\'s name, then each mode\'s name from the product\'s catalogue: "System", "Light", "Dark".',
|
|
587
587
|
access: "A radio group named by `aria-label`; each mode is a radio with its name, moved through with the arrow keys.",
|
|
588
588
|
},
|
|
@@ -780,7 +780,7 @@ export const USAGE = [
|
|
|
780
780
|
summary: 'A record\'s identity in a row (DESIGN.md "Tables and missing values"): its mark at 32, its name, and under it one muted `detail` line saying what the record is or where it comes from, so the row reads without a column heading.',
|
|
781
781
|
when: "The first cell of every collection row: the mark, the name, and one muted line saying what the record is or where it comes from, so the row reads without a heading.",
|
|
782
782
|
instead: "An Agent in a gallery is `AgentListRow`; a model is `ModelIdentity`. A descriptive item with prose and actions is `Item`.",
|
|
783
|
-
behaviour: "The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark`
|
|
783
|
+
behaviour: "The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot, so the record stays the name over one line. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.",
|
|
784
784
|
words: '“Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity) Ours: the name as the record calls itself, and the detail in sentence case: kind, then relation, three to six words, "Company skill · every Agent has it".',
|
|
785
785
|
access: "The name is the row link's text; the mark is decorative; the title carries the full name.",
|
|
786
786
|
},
|
|
@@ -813,7 +813,7 @@ export const USAGE = [
|
|
|
813
813
|
summary: "One Agent in a list of them: the orb, the name, and the line that says what the Agent is doing now or what it is for.",
|
|
814
814
|
when: "Nadia's Agents in her window's sidebar, and a website scene that shows a list of Agents.",
|
|
815
815
|
instead: "An Agent as a record among others in a page's list is a RecordList row with an agent IdentityMark; an Agent's own page heads with ObjectPage.",
|
|
816
|
-
behaviour: "It is the row's presentation alone: the consumer owns the control around it (Nadia's button that opens the Agent, a scene's list item that opens nothing). The current row's name is set in 500. A state tone draws the state line as a small StatusBadge; without a tone it stays plain text. Narrow, the orb stands alone and the words stay for a screen reader; the consumer names it with its state line in a tooltip, as Nadia's list does. Only the orb moves in the list.
|
|
816
|
+
behaviour: "It is the row's presentation alone: the consumer owns the control around it (Nadia's button that opens the Agent, a scene's list item that opens nothing). The current row's name is set in 500. A state tone draws the state line as a small StatusBadge; without a tone it stays plain text. Narrow, the orb stands alone and the words stay for a screen reader; the consumer names it with its state line in a tooltip, as Nadia's list does. Only the orb moves in the list. The name is 14 px and the state line 12 px at every size. The drawn orb leaves about 10 px of its 64 px box empty, so at 64 px that empty edge overlaps the row's padding (6 px above and below, 4 px at the start) and the words stand 4 px from the box: the row hugs the visible orb and the name sits beside it, as beside a solid picture. In Nadia's list her orb is 64 px in her real state, and 40 px in the narrow column from 640 to 899 px.",
|
|
817
817
|
words: 'The name, then one state line: what she is doing now ("Answering on Telegram", "Working") or what she is for ("Sorts requests"). The tone never says more than the line does. Nadia\'s list says "Working", "Idle" or "Needs you"; the badge marks a state that needs the person ("Needs you", a failed set-up), and the orb shows working by itself.',
|
|
818
818
|
access: "The orb is decorative; the name and the state line are the row's text, read by its control. StatusBadge owns its decorative mark, so the line carries its meaning.",
|
|
819
819
|
},
|
|
@@ -1407,7 +1407,7 @@ export const USAGE = [
|
|
|
1407
1407
|
summary: "The account menu at the foot of a shell's navigation: whose account it is, and the menu opening on that same identity with what they can do beneath it.",
|
|
1408
1408
|
when: "The account at the sidebar's foot in the Portal, Nadia and Alveare: the gear to the account's settings, Theme, Language, a product's own settings, its commands, and Sign out.",
|
|
1409
1409
|
instead: "A record's or a page's commands are an ActionMenu; a setting with more than a few options or one that needs a Save belongs on a SettingsPage.",
|
|
1410
|
-
behaviour: "The menu opens 6 px above its trigger, 288 px wide, as Vercel's does: a header of the name and every line under it with the gear at its end and no picture, one divider, then 36 px rows, each its label leading and its icon or control trailing in muted ink: Theme and Language as small round pills, the product's choices as submenus, its commands with any reading before
|
|
1410
|
+
behaviour: "The menu opens 6 px above its trigger, 288 px wide, as Vercel's does: a header of the name and every line under it with the gear at its end and no picture, one divider, then 36 px rows, each its label leading and its icon or control trailing in muted ink: the product's leading commands first, Theme and Language as small round pills, the product's choices as submenus, its other commands, each command with any reading before its icon, and Sign out last in the body colour, since signing out destroys nothing. Ruled by the owner on 16 October 2026; on 19 October 2026 Nadia, a desktop app, also gets a Settings row after Language. Theme and Language apply at once and keep the menu open. While signing out, Sign out says its pending word, the menu stays open and the trigger is locked; a failure shows under it. A Sign out that `confirms` closes the menu instead, for the product's own confirmation.",
|
|
1411
1411
|
words: 'The menu\'s name ("Account"), the person\'s name and lines, then each row\'s noun or verb from the product\'s catalogue: "Theme", "Language", "Usage", "Sign out" and "Signing out".',
|
|
1412
1412
|
access: 'The trigger and the menu are named "{label}: {name}"; the gear carries its label; the pending sign-out is aria-busy and announced through a status; a failure is an alert.',
|
|
1413
1413
|
},
|
|
@@ -1429,7 +1429,7 @@ export const USAGE = [
|
|
|
1429
1429
|
summary: "A record's three dots: the menu of its secondary commands, headed by the record's name.",
|
|
1430
1430
|
when: '"An overflow menu triggered by a three-dot icon that reveals additional actions in a dropdown." (https://vercel.com/geist/dots-menu) "Use Menu for a discoverable trigger that opens a list of actions on a single resource (a dots menu on a row, a dropdown on a primary entity)." (https://vercel.com/geist/menu) Ours: the secondary commands of one record from a visible trigger: the row\'s three dots, or the page\'s, which comes last in the title row. A record\'s one main task is a visible button beside the dots; one non-destructive command that can run, alone, is drawn as a button by `RecordList` and `PageActions`, never as a menu; one that cannot run or is locked stays in the menu so its reason is read.',
|
|
1431
1431
|
instead: '"For right-click or long-press on a row, use `ContextMenu`. For global commands behind `⌘K`, use `CommandMenu`. For two related primary actions, use a split button rather than burying the secondary action." (https://vercel.com/geist/menu) Ours: for a record\'s main task use a visible button (Edit opens the row). For finding across everything use `CommandDialog`. For a value to choose use `Select`. For right-click use the mirror, `ActionContextMenu`.',
|
|
1432
|
-
behaviour: '"Open on click, not hover; hover-open menus collide with screen readers and trackpad scrolls." (https://vercel.com/geist/menu) "Position auto-flips based on window bounds; don’t hardcode a side that clips on narrow viewports." (https://vercel.com/geist/menu) "Close on item activation, Escape, and outside-click. Don’t auto-close on a hover-out." (https://vercel.com/geist/menu) "Use `MenuItemLocked` for permission-gated actions so the lock icon and disabled state explain why the row is inert." (https://vercel.com/geist/menu) Ours: that is `locked` with its `reason`, which is required: the row stays in place, inert, with a lock at its end and the reason under its label. The trigger is a ghost icon button at `{spacing.control-height}` (36 px) wherever it stands, named for its record ("More for pec@acme.it"), and comes last, after any button; it keeps its fill while open. The menu opens 10 px under the trigger, aligned to its end: the record\'s name, the commands with their icons first and the value one acts on in mono under it, then the destructive ones after a divider in the error colour. A command that cannot run now takes `disabledReason`: it stays in place, 36 px like the rest, with its text muted, never dimmed, and says why in a line under its label; a destructive one is muted, not red; a running one takes `pending` and `pendingLabel`. Nothing renders with no command.',
|
|
1432
|
+
behaviour: '"Open on click, not hover; hover-open menus collide with screen readers and trackpad scrolls." (https://vercel.com/geist/menu) "Position auto-flips based on window bounds; don’t hardcode a side that clips on narrow viewports." (https://vercel.com/geist/menu) "Close on item activation, Escape, and outside-click. Don’t auto-close on a hover-out." (https://vercel.com/geist/menu) "Use `MenuItemLocked` for permission-gated actions so the lock icon and disabled state explain why the row is inert." (https://vercel.com/geist/menu) Ours: that is `locked` with its `reason`, which is required: the row stays in place, inert, with a lock at its end and the reason under its label. The menu is at most 288 px wide, as the account menu is, and the record\'s name heading it is one line, cut with an ellipsis and whole in its tooltip, so a long record, a memory or a sentence, never widens it. The trigger is a ghost icon button at `{spacing.control-height}` (36 px) wherever it stands, named for its record ("More for pec@acme.it"), and comes last, after any button; it keeps its fill while open. The menu opens 10 px under the trigger, aligned to its end: the record\'s name, the commands with their icons first and the value one acts on in mono under it, then the destructive ones after a divider in the error colour. A command that cannot run now takes `disabledReason`: it stays in place, 36 px like the rest, with its text muted, never dimmed, and says why in a line under its label; a destructive one is muted, not red; a running one takes `pending` and `pendingLabel`. Nothing renders with no command.',
|
|
1433
1433
|
words: '"Item children are Title Case `Verb + Noun` (`Rename Project`, `Duplicate Deployment`). Bare verbs like `Rename` or `Edit` are wrong outside obvious single-object context." (https://vercel.com/geist/menu) "End an item with `…` only when activating it opens a follow-up dialog (`Rename…`, `Transfer to Team…`)." (https://vercel.com/geist/menu) "Group destructive items at the bottom, separated by a divider, and keep the destructive copy as `Verb + Noun` (`Delete Project`, never bare `Delete`)." (https://vercel.com/geist/menu) "Cap a Menu around 10 items. Past that, group with `MenuSection` or move secondary actions to a settings page." (https://vercel.com/geist/menu) Ours: sentence case, "Remove mailbox", not Title Case. The trigger reads "More for {record}"; the header is the record\'s name. Rows wrap, never truncate.',
|
|
1434
1434
|
access: '"Typeahead jumps to the next item whose label starts with the typed character; keep the visible label first so typeahead matches what the user sees." (https://vercel.com/geist/menu) "Return focus to the trigger on close so keyboard users keep their place in the row." (https://vercel.com/geist/menu) Ours: the trigger is a ghost icon button named by `label`, which is required, `aria-expanded` while open. `role="menu"`, items `menuitem` in a group named by `record`, which is required; every command requires its `icon`. The value a command acts on and its reason are the item\'s description; icons hidden.',
|
|
1435
1435
|
},
|
|
@@ -1811,8 +1811,8 @@ export const USAGE = [
|
|
|
1811
1811
|
name: "ReadingColumn",
|
|
1812
1812
|
module: "reading-column",
|
|
1813
1813
|
family: "Other",
|
|
1814
|
-
summary: "The column a page's content stands in: full, reading or
|
|
1815
|
-
when: "Setting a page's measure: `reading` for prose and conversations, `
|
|
1814
|
+
summary: "The column a page's content stands in: full, reading or settings width.",
|
|
1815
|
+
when: "Setting a page's measure: `reading` (768 px) for prose and conversations, `settings` (960 px) for a page of settings, as Vercel's settings stop, and `full` (1280 px, the Portal shell's width) for collections, which fill the page as Vercel's lists do.",
|
|
1816
1816
|
instead: "A settings page's column is `SettingsPage`'s. A card row is `CardRow`.",
|
|
1817
1817
|
behaviour: "Centred with a maximum width per kind; full takes the content's width.",
|
|
1818
1818
|
words: "None of its own.",
|