@westonkd/sprint 0.6.0 → 0.8.0
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/README.md +16 -5
- package/agent-manifest.json +1391 -83
- package/dist/index.cjs +283 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1371 -44
- package/dist/index.js +5299 -2545
- package/dist/index.js.map +1 -1
- package/dist/sprint.css +1 -1
- package/package.json +1 -1
package/agent-manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"library": "sprint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"conventions": {
|
|
5
5
|
"componentAttribute": "data-sprint",
|
|
6
6
|
"partAttribute": "data-sprint-part",
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
],
|
|
42
42
|
"default": "info"
|
|
43
43
|
},
|
|
44
|
+
"announce": {
|
|
45
|
+
"kind": "enum",
|
|
46
|
+
"description": "Override how assistive technology announces the alert. assertive renders role=alert and interrupts; polite renders role=status and waits. Defaults to assertive for danger and warning and polite otherwise. Pass polite for a standing warning, such as an offline notice, that should not interrupt.",
|
|
47
|
+
"values": [
|
|
48
|
+
"assertive",
|
|
49
|
+
"polite"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
44
52
|
"onDismiss": {
|
|
45
53
|
"kind": "handler",
|
|
46
54
|
"description": "Called when the dismiss control is pressed. Providing it renders the control and registers the dismiss tool; the page owns removing the alert."
|
|
@@ -103,11 +111,87 @@
|
|
|
103
111
|
"title": "A failure",
|
|
104
112
|
"description": "Danger announces assertively via role=alert.",
|
|
105
113
|
"code": "<Alert tone=\"danger\" label=\"Sign-in failed\">Wrong callsign or access code.</Alert>"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"title": "A standing warning",
|
|
117
|
+
"description": "A warning that stays on screen while a condition holds announces politely, so it does not interrupt whatever the person is reading.",
|
|
118
|
+
"code": "<Alert tone=\"warning\" announce=\"polite\" label=\"Offline\">Changes are saved on this device until the connection returns.</Alert>"
|
|
106
119
|
}
|
|
107
120
|
],
|
|
108
121
|
"a11y": {
|
|
109
122
|
"role": "status",
|
|
110
|
-
"notes": "Danger and warning render role=alert and announce assertively; info and neutral render role=status. The dismiss control is a labelled button. Render the alert when the condition occurs rather than toggling its visibility, or the announcement is lost."
|
|
123
|
+
"notes": "Danger and warning render role=alert and announce assertively; info and neutral render role=status, unless announce overrides it. The dismiss control is a labelled button. Render the alert when the condition occurs rather than toggling its visibility, or the announcement is lost."
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "Avatar",
|
|
128
|
+
"category": "display",
|
|
129
|
+
"summary": "A person's photo in a circle, falling back to their initials when there is no photo or it fails to load.",
|
|
130
|
+
"whenToUse": "Use beside a person's name in a row, a card, or an account menu trigger. Pass the full name; it is the accessible name and the source of the initials. Set decorative when the name is already printed right beside it, so it is not read twice.",
|
|
131
|
+
"whenNotToUse": "Do not use for a logo or an illustration; that is an Image. Do not use as a button on its own; put it inside a Button or a Menu trigger that carries the label.",
|
|
132
|
+
"status": "experimental",
|
|
133
|
+
"props": {
|
|
134
|
+
"name": {
|
|
135
|
+
"kind": "string",
|
|
136
|
+
"description": "The person's name. Names the avatar and supplies the initials.",
|
|
137
|
+
"required": true
|
|
138
|
+
},
|
|
139
|
+
"src": {
|
|
140
|
+
"kind": "string",
|
|
141
|
+
"description": "The photo's URL. Without it, or if it fails to load, the initials show instead."
|
|
142
|
+
},
|
|
143
|
+
"size": {
|
|
144
|
+
"kind": "enum",
|
|
145
|
+
"description": "small for dense rows, medium beside body text, large in a profile header.",
|
|
146
|
+
"values": [
|
|
147
|
+
"small",
|
|
148
|
+
"medium",
|
|
149
|
+
"large"
|
|
150
|
+
],
|
|
151
|
+
"default": "medium"
|
|
152
|
+
},
|
|
153
|
+
"decorative": {
|
|
154
|
+
"kind": "boolean",
|
|
155
|
+
"description": "Hide the avatar from assistive technology and the agent view, for when the name is printed beside it.",
|
|
156
|
+
"default": false
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"state": {
|
|
160
|
+
"photo": {
|
|
161
|
+
"description": "Present while a photo is showing rather than initials.",
|
|
162
|
+
"attribute": "data-sprint-photo"
|
|
163
|
+
},
|
|
164
|
+
"size": {
|
|
165
|
+
"description": "The size, when it is not medium.",
|
|
166
|
+
"attribute": "data-sprint-size",
|
|
167
|
+
"values": [
|
|
168
|
+
"small",
|
|
169
|
+
"large"
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"agentView": {
|
|
174
|
+
"example": "- **Avatar** \"Ada Okafor\" [photo]"
|
|
175
|
+
},
|
|
176
|
+
"examples": [
|
|
177
|
+
{
|
|
178
|
+
"title": "A photo",
|
|
179
|
+
"code": "<Avatar name=\"Ada Okafor\" src=\"media/portrait.svg\" />"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"title": "Initials when there is no photo",
|
|
183
|
+
"description": "The first and last initials, on the inset surface.",
|
|
184
|
+
"code": "<Avatar name=\"Brother Lind\" size=\"large\" />"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"title": "Beside a printed name",
|
|
188
|
+
"description": "decorative keeps a screen reader and an agent from hearing the name twice.",
|
|
189
|
+
"code": "<Stack direction=\"row\" gap=\"snug\" align=\"center\">\n <Avatar name=\"Sister Amaral\" size=\"small\" decorative />\n <Text as=\"span\">Sister Amaral</Text>\n</Stack>"
|
|
190
|
+
}
|
|
191
|
+
],
|
|
192
|
+
"a11y": {
|
|
193
|
+
"role": "img",
|
|
194
|
+
"notes": "A role=img element named by the person's name, with the photo's own alt left empty so the name is read once. decorative switches it to aria-hidden."
|
|
111
195
|
}
|
|
112
196
|
},
|
|
113
197
|
{
|
|
@@ -313,6 +397,28 @@
|
|
|
313
397
|
],
|
|
314
398
|
"default": "neutral"
|
|
315
399
|
},
|
|
400
|
+
"size": {
|
|
401
|
+
"kind": "enum",
|
|
402
|
+
"description": "medium for ordinary forms and toolbars; small for dense surfaces such as table rows, cards and toasts.",
|
|
403
|
+
"values": [
|
|
404
|
+
"medium",
|
|
405
|
+
"small"
|
|
406
|
+
],
|
|
407
|
+
"default": "medium"
|
|
408
|
+
},
|
|
409
|
+
"icon": {
|
|
410
|
+
"kind": "node",
|
|
411
|
+
"description": "An icon drawn before the label, sized to the text. It is decorative and hidden from assistive technology; the label still names the button."
|
|
412
|
+
},
|
|
413
|
+
"iconEnd": {
|
|
414
|
+
"kind": "node",
|
|
415
|
+
"description": "An icon drawn after the label, such as a chevron on a button that opens something. Ignored when hideLabel is set."
|
|
416
|
+
},
|
|
417
|
+
"hideLabel": {
|
|
418
|
+
"kind": "boolean",
|
|
419
|
+
"description": "Show only the icon, as a square button. The label stays in the page for screen readers and agents, names the press tool, and appears as a tooltip on hover and focus. Requires icon.",
|
|
420
|
+
"default": false
|
|
421
|
+
},
|
|
316
422
|
"block": {
|
|
317
423
|
"kind": "boolean",
|
|
318
424
|
"description": "Render as a full-width bar. Combine with tone=\"action\" for the primary action of a region.",
|
|
@@ -352,6 +458,13 @@
|
|
|
352
458
|
"danger"
|
|
353
459
|
]
|
|
354
460
|
},
|
|
461
|
+
"size": {
|
|
462
|
+
"description": "Present as small on a compact button.",
|
|
463
|
+
"attribute": "data-sprint-size",
|
|
464
|
+
"values": [
|
|
465
|
+
"small"
|
|
466
|
+
]
|
|
467
|
+
},
|
|
355
468
|
"block": {
|
|
356
469
|
"description": "Present when the button renders as a full-width bar.",
|
|
357
470
|
"attribute": "data-sprint-block"
|
|
@@ -383,6 +496,16 @@
|
|
|
383
496
|
"example": "- **Button** \"Prepare launch\" [tone=action] → tool `press-prepare-launch`"
|
|
384
497
|
},
|
|
385
498
|
"examples": [
|
|
499
|
+
{
|
|
500
|
+
"title": "An icon-only button",
|
|
501
|
+
"description": "hideLabel draws only the icon. The label still names the button and its tool, and shows as a tooltip, so the control is never a mystery.",
|
|
502
|
+
"code": "<Button icon={<SidebarIcon />} hideLabel onClick={collapse}>\n Hide sidebar\n</Button>"
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
"title": "A compact button with an icon",
|
|
506
|
+
"description": "size=\"small\" for a dense row; the icon sits before the label.",
|
|
507
|
+
"code": "<Button size=\"small\" icon={<PlusIcon />} onClick={addSpeaker}>\n Add speaker\n</Button>"
|
|
508
|
+
},
|
|
386
509
|
{
|
|
387
510
|
"title": "Primary action",
|
|
388
511
|
"description": "The one rationed acid action bar for a view.",
|
|
@@ -610,6 +733,11 @@
|
|
|
610
733
|
"description": "Whether the box is checked. The box is fully controlled.",
|
|
611
734
|
"required": true
|
|
612
735
|
},
|
|
736
|
+
"indeterminate": {
|
|
737
|
+
"kind": "boolean",
|
|
738
|
+
"description": "Show the box as partly checked, for a parent box whose children are some checked and some not. It overrides checked visually and reads as checked=mixed to agents and assistive technology. Pressing a mixed box calls onChange(true); the page then clears indeterminate.",
|
|
739
|
+
"default": false
|
|
740
|
+
},
|
|
613
741
|
"onChange": {
|
|
614
742
|
"kind": "handler",
|
|
615
743
|
"description": "Called with the new checked state. The set tool drives a real click, so this runs for agent changes too.",
|
|
@@ -649,7 +777,7 @@
|
|
|
649
777
|
},
|
|
650
778
|
"state": {
|
|
651
779
|
"checked": {
|
|
652
|
-
"description": "Present while the box is checked.",
|
|
780
|
+
"description": "Present while the box is checked, and \"mixed\" while it is indeterminate.",
|
|
653
781
|
"attribute": "data-sprint-checked"
|
|
654
782
|
},
|
|
655
783
|
"disabled": {
|
|
@@ -705,6 +833,11 @@
|
|
|
705
833
|
"title": "A disabled box",
|
|
706
834
|
"description": "Disabled unregisters the tool, so an agent cannot change what a person could not.",
|
|
707
835
|
"code": "<Checkbox\n label=\"Telemetry\"\n checked\n disabled\n onChange={setTelemetry}\n/>"
|
|
836
|
+
},
|
|
837
|
+
{
|
|
838
|
+
"title": "A partly picked group",
|
|
839
|
+
"description": "A parent box over a group shows mixed while only some of the group is picked. Pressing it picks everything.",
|
|
840
|
+
"code": "<Checkbox\n label=\"Whole crew\"\n checked={picked.length === crew.length}\n indeterminate={picked.length > 0 && picked.length < crew.length}\n onChange={(next) => setPicked(next ? crew : [])}\n/>"
|
|
708
841
|
}
|
|
709
842
|
],
|
|
710
843
|
"a11y": {
|
|
@@ -941,6 +1074,203 @@
|
|
|
941
1074
|
"notes": "The frame is a figure named by its caption. The copy control is a real button and reports back in its own label once the snippet is on the clipboard; the label swap is a polite live region, so a screen reader hears the confirmation too."
|
|
942
1075
|
}
|
|
943
1076
|
},
|
|
1077
|
+
{
|
|
1078
|
+
"name": "Combobox",
|
|
1079
|
+
"category": "input",
|
|
1080
|
+
"summary": "A searchable single-choice field for long lists: type to filter, arrow to an option, Enter to pick. Options can be grouped, described, limited per group and rendered your own way. Registers one choose tool that takes a label or a search.",
|
|
1081
|
+
"whenToUse": "Use when there are too many options to scan: a member picker over hundreds of people, a calling picker grouped by organization, a board filter. Options hold data: { value, label, group?, description?, keywords?, disabled? }. The default filter matches every word of the query against the label, description and keywords, ignoring case and accents. For a server-side search, pass filter={false}, update options from onQueryChange, and set loading while results arrive.",
|
|
1082
|
+
"whenNotToUse": "Do not use for a short list a person can scan at once; that is a Select, or a SegmentedControl for two to four options. Do not use for free text that only suggests; this field only accepts its options. Do not use for choosing several values.",
|
|
1083
|
+
"status": "experimental",
|
|
1084
|
+
"props": {
|
|
1085
|
+
"label": {
|
|
1086
|
+
"kind": "string",
|
|
1087
|
+
"description": "What is being chosen, such as \"Member\". Names the field and derives the tool name.",
|
|
1088
|
+
"required": true
|
|
1089
|
+
},
|
|
1090
|
+
"options": {
|
|
1091
|
+
"kind": "array",
|
|
1092
|
+
"description": "The options: { value, label, group?, description?, keywords?, disabled? }. Consecutive and non-consecutive options sharing a group are gathered under one heading, in the order groups first appear. keywords are extra search terms that are never shown.",
|
|
1093
|
+
"required": true
|
|
1094
|
+
},
|
|
1095
|
+
"value": {
|
|
1096
|
+
"kind": "string",
|
|
1097
|
+
"description": "The chosen option's value, or an empty string for none. Fully controlled.",
|
|
1098
|
+
"required": true
|
|
1099
|
+
},
|
|
1100
|
+
"onChange": {
|
|
1101
|
+
"kind": "handler",
|
|
1102
|
+
"description": "Called with the chosen value, or an empty string when cleared.",
|
|
1103
|
+
"required": true
|
|
1104
|
+
},
|
|
1105
|
+
"placeholder": {
|
|
1106
|
+
"kind": "string",
|
|
1107
|
+
"description": "Ghost text while nothing is chosen or while searching."
|
|
1108
|
+
},
|
|
1109
|
+
"hint": {
|
|
1110
|
+
"kind": "string",
|
|
1111
|
+
"description": "Guidance under the field. Replaced by error while one is set."
|
|
1112
|
+
},
|
|
1113
|
+
"error": {
|
|
1114
|
+
"kind": "string",
|
|
1115
|
+
"description": "A validation message that marks the field invalid."
|
|
1116
|
+
},
|
|
1117
|
+
"name": {
|
|
1118
|
+
"kind": "string",
|
|
1119
|
+
"description": "The native form name the chosen value submits under."
|
|
1120
|
+
},
|
|
1121
|
+
"disabled": {
|
|
1122
|
+
"kind": "boolean",
|
|
1123
|
+
"description": "Disable the field and unregister the choose tool.",
|
|
1124
|
+
"default": false
|
|
1125
|
+
},
|
|
1126
|
+
"required": {
|
|
1127
|
+
"kind": "boolean",
|
|
1128
|
+
"description": "Mark the field required. A required field is not clearable by default.",
|
|
1129
|
+
"default": false
|
|
1130
|
+
},
|
|
1131
|
+
"hideLabel": {
|
|
1132
|
+
"kind": "boolean",
|
|
1133
|
+
"description": "Hide the label visually while keeping it for screen readers and agents. Pair it with a placeholder.",
|
|
1134
|
+
"default": false
|
|
1135
|
+
},
|
|
1136
|
+
"clearable": {
|
|
1137
|
+
"kind": "boolean",
|
|
1138
|
+
"description": "Show a clear control while something is chosen. Defaults to not required."
|
|
1139
|
+
},
|
|
1140
|
+
"clearLabel": {
|
|
1141
|
+
"kind": "string",
|
|
1142
|
+
"description": "Accessible name of the clear control.",
|
|
1143
|
+
"default": "Clear"
|
|
1144
|
+
},
|
|
1145
|
+
"loading": {
|
|
1146
|
+
"kind": "boolean",
|
|
1147
|
+
"description": "Show a small spinner in the field while options are being fetched.",
|
|
1148
|
+
"default": false
|
|
1149
|
+
},
|
|
1150
|
+
"filter": {
|
|
1151
|
+
"kind": "handler",
|
|
1152
|
+
"description": "(option, query) => boolean, replacing the built-in word match. Pass false when the options are already the results of a search the page ran."
|
|
1153
|
+
},
|
|
1154
|
+
"onQueryChange": {
|
|
1155
|
+
"kind": "handler",
|
|
1156
|
+
"description": "Called with the search text as it changes, and with an empty string on close."
|
|
1157
|
+
},
|
|
1158
|
+
"groupLimit": {
|
|
1159
|
+
"kind": "number",
|
|
1160
|
+
"description": "Show at most this many options per group, with a \"more; keep typing\" note for the rest, so one large group cannot bury the others."
|
|
1161
|
+
},
|
|
1162
|
+
"limit": {
|
|
1163
|
+
"kind": "number",
|
|
1164
|
+
"description": "Show at most this many options in total.",
|
|
1165
|
+
"default": 200
|
|
1166
|
+
},
|
|
1167
|
+
"emptyLabel": {
|
|
1168
|
+
"kind": "string",
|
|
1169
|
+
"description": "What the list says when nothing matches.",
|
|
1170
|
+
"default": "No matches"
|
|
1171
|
+
},
|
|
1172
|
+
"renderOption": {
|
|
1173
|
+
"kind": "handler",
|
|
1174
|
+
"description": "(option) => ReactNode, drawing an option your own way, such as with an Avatar. It is the human rendering only: the label is still what is searched, announced and offered to agents."
|
|
1175
|
+
},
|
|
1176
|
+
"inputRef": {
|
|
1177
|
+
"kind": "object",
|
|
1178
|
+
"description": "A ref to the underlying input, for focusing it."
|
|
1179
|
+
},
|
|
1180
|
+
"agentName": {
|
|
1181
|
+
"kind": "string",
|
|
1182
|
+
"description": "Override the label used to derive the tool name."
|
|
1183
|
+
},
|
|
1184
|
+
"agentTool": {
|
|
1185
|
+
"kind": "boolean",
|
|
1186
|
+
"description": "Set false to render the field without registering the choose tool.",
|
|
1187
|
+
"default": true
|
|
1188
|
+
}
|
|
1189
|
+
},
|
|
1190
|
+
"state": {
|
|
1191
|
+
"value": {
|
|
1192
|
+
"description": "The chosen option's label.",
|
|
1193
|
+
"attribute": "data-sprint-value"
|
|
1194
|
+
},
|
|
1195
|
+
"empty": {
|
|
1196
|
+
"description": "Present while nothing is chosen.",
|
|
1197
|
+
"attribute": "data-sprint-empty"
|
|
1198
|
+
},
|
|
1199
|
+
"options": {
|
|
1200
|
+
"description": "How many options the field holds.",
|
|
1201
|
+
"attribute": "data-sprint-options"
|
|
1202
|
+
},
|
|
1203
|
+
"loading": {
|
|
1204
|
+
"description": "Present while options are being fetched.",
|
|
1205
|
+
"attribute": "data-sprint-loading"
|
|
1206
|
+
},
|
|
1207
|
+
"disabled": {
|
|
1208
|
+
"description": "Present when the field cannot be changed.",
|
|
1209
|
+
"attribute": "data-sprint-disabled"
|
|
1210
|
+
},
|
|
1211
|
+
"required": {
|
|
1212
|
+
"description": "Present when a choice is needed.",
|
|
1213
|
+
"attribute": "data-sprint-required"
|
|
1214
|
+
},
|
|
1215
|
+
"invalid": {
|
|
1216
|
+
"description": "Present while an error is set.",
|
|
1217
|
+
"attribute": "data-sprint-invalid"
|
|
1218
|
+
}
|
|
1219
|
+
},
|
|
1220
|
+
"tools": {
|
|
1221
|
+
"choose": {
|
|
1222
|
+
"verb": "choose",
|
|
1223
|
+
"description": "Choose an option in this searchable field. Pass an option's exact label to select it. Pass part of a label to search: one match is selected, several are listed back so you can call again with the exact label. Pass an empty string to clear a field that allows clearing. Returns the field's state after the change.",
|
|
1224
|
+
"inputSchema": {
|
|
1225
|
+
"type": "object",
|
|
1226
|
+
"properties": {
|
|
1227
|
+
"option": {
|
|
1228
|
+
"type": "string",
|
|
1229
|
+
"description": "An exact option label, a search for one, or an empty string to clear the field."
|
|
1230
|
+
}
|
|
1231
|
+
},
|
|
1232
|
+
"required": [
|
|
1233
|
+
"option"
|
|
1234
|
+
]
|
|
1235
|
+
},
|
|
1236
|
+
"readOnly": false,
|
|
1237
|
+
"untrustedContent": true,
|
|
1238
|
+
"registeredWhen": "The field is mounted and enabled, has a resolvable label, and no other component claims the same tool name.",
|
|
1239
|
+
"unregisteredWhen": "The field unmounts or becomes disabled."
|
|
1240
|
+
}
|
|
1241
|
+
},
|
|
1242
|
+
"agentView": {
|
|
1243
|
+
"example": "- **Combobox** \"Member\" [empty, options=312, listed=50] → tool `choose-member`\n - part `option` \"Ada Okafor\" [group=Elders quorum]"
|
|
1244
|
+
},
|
|
1245
|
+
"examples": [
|
|
1246
|
+
{
|
|
1247
|
+
"title": "A grouped member picker",
|
|
1248
|
+
"description": "Hundreds of members grouped by organization, at most five per group until the person types. An agent passes a name, or part of one, to the choose tool.",
|
|
1249
|
+
"code": "<Combobox\n label=\"Member\"\n placeholder=\"Search members\"\n value={memberId}\n onChange={setMemberId}\n groupLimit={5}\n options={members.map((member) => ({\n value: member.id,\n label: member.name,\n group: member.organization,\n keywords: [member.preferredName],\n }))}\n/>"
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
"title": "Options drawn your own way",
|
|
1253
|
+
"description": "renderOption draws each option with an avatar; the label still drives search, announcement and the agent view.",
|
|
1254
|
+
"code": "<Combobox\n label=\"Speaker\"\n value={speaker}\n onChange={setSpeaker}\n options={members}\n renderOption={(option) => (\n <Stack direction=\"row\" gap=\"snug\" align=\"center\">\n <Avatar name={option.label} size=\"small\" decorative />\n <Text as=\"span\">{option.label}</Text>\n </Stack>\n )}\n/>"
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
"title": "A server-side search",
|
|
1258
|
+
"description": "filter={false} trusts the options as given; the page searches on each onQueryChange and shows the spinner meanwhile.",
|
|
1259
|
+
"code": "<Combobox\n label=\"Calling\"\n value={calling}\n onChange={setCalling}\n filter={false}\n onQueryChange={search}\n loading={searching}\n options={results}\n error={calling === \"\" ? \"Choose a calling.\" : undefined}\n required\n/>"
|
|
1260
|
+
}
|
|
1261
|
+
],
|
|
1262
|
+
"a11y": {
|
|
1263
|
+
"role": "combobox",
|
|
1264
|
+
"keyboard": [
|
|
1265
|
+
"Typing filters and opens the list",
|
|
1266
|
+
"Down and Up Arrow open the list and move between enabled options",
|
|
1267
|
+
"Ctrl+Home and Ctrl+End jump to the first and last option",
|
|
1268
|
+
"Enter chooses the highlighted option",
|
|
1269
|
+
"Escape closes the list and restores the chosen label"
|
|
1270
|
+
],
|
|
1271
|
+
"notes": "An editable combobox with list autocomplete. Focus stays in the input and aria-activedescendant follows the highlighted option. Groups are labelled role=group sections. The list is a popover in the top layer, rendered in place, so it opens above a modal Dialog and flips above the field when there is no room below."
|
|
1272
|
+
}
|
|
1273
|
+
},
|
|
944
1274
|
{
|
|
945
1275
|
"name": "CopyField",
|
|
946
1276
|
"category": "input",
|
|
@@ -1118,6 +1448,20 @@
|
|
|
1118
1448
|
"4"
|
|
1119
1449
|
]
|
|
1120
1450
|
},
|
|
1451
|
+
"size": {
|
|
1452
|
+
"kind": "enum",
|
|
1453
|
+
"description": "The dialog's width: small (26rem) for a confirmation, medium (36rem) for a short form, large (52rem) for a table or a two-column form. Every size shrinks to fit a phone.",
|
|
1454
|
+
"values": [
|
|
1455
|
+
"small",
|
|
1456
|
+
"medium",
|
|
1457
|
+
"large"
|
|
1458
|
+
],
|
|
1459
|
+
"default": "small"
|
|
1460
|
+
},
|
|
1461
|
+
"initialFocus": {
|
|
1462
|
+
"kind": "object",
|
|
1463
|
+
"description": "A ref to the element that takes focus when the dialog opens, such as the first field of a form or the safe choice in a destructive confirmation. Without it the browser focuses the first focusable element, which is the close control."
|
|
1464
|
+
},
|
|
1121
1465
|
"owner": {
|
|
1122
1466
|
"kind": "string",
|
|
1123
1467
|
"description": "The tool name of the control that opened this dialog. Published as data-sprint-owner so a reading agent can attach the dialog to its opener."
|
|
@@ -1136,6 +1480,14 @@
|
|
|
1136
1480
|
"open": {
|
|
1137
1481
|
"description": "Present while the dialog is shown. A closed dialog is absent from the DOM entirely.",
|
|
1138
1482
|
"attribute": "data-sprint-open"
|
|
1483
|
+
},
|
|
1484
|
+
"size": {
|
|
1485
|
+
"description": "The width the dialog was given, when it is not the default small.",
|
|
1486
|
+
"attribute": "data-sprint-size",
|
|
1487
|
+
"values": [
|
|
1488
|
+
"medium",
|
|
1489
|
+
"large"
|
|
1490
|
+
]
|
|
1139
1491
|
}
|
|
1140
1492
|
},
|
|
1141
1493
|
"tools": {
|
|
@@ -1165,6 +1517,11 @@
|
|
|
1165
1517
|
"title": "Owned by its opener",
|
|
1166
1518
|
"description": "Passing the opener's tool name lets a reading agent attach the dialog to the control that produced it.",
|
|
1167
1519
|
"code": "<Dialog\n label=\"Rotate secret\"\n open={rotating}\n owner=\"press-rotate-secret\"\n onClose={() => setRotating(false)}\n>\n <Text>The current secret keeps working for one hour.</Text>\n</Dialog>"
|
|
1520
|
+
},
|
|
1521
|
+
{
|
|
1522
|
+
"title": "A form that focuses its first field",
|
|
1523
|
+
"description": "A medium dialog for a short form. initialFocus puts the cursor in the field instead of on the close control, so the person can type straight away.",
|
|
1524
|
+
"code": "<Dialog\n label=\"Rename station\"\n size=\"medium\"\n open={renaming}\n initialFocus={nameField}\n onClose={() => setRenaming(false)}\n>\n <Stack gap=\"tight\">\n <TextInput label=\"Station name\" value={name} onChange={setName} inputRef={nameField} />\n <Button tone=\"action\" onClick={save}>Save name</Button>\n </Stack>\n</Dialog>"
|
|
1168
1525
|
}
|
|
1169
1526
|
],
|
|
1170
1527
|
"a11y": {
|
|
@@ -1347,7 +1704,7 @@
|
|
|
1347
1704
|
},
|
|
1348
1705
|
{
|
|
1349
1706
|
"title": "The page's main break",
|
|
1350
|
-
"description": "A band draws the theme's own ornament: hatching in the default register, pins in calorie, a barcode strip in trax. Use it once per page.",
|
|
1707
|
+
"description": "A band draws the theme's own ornament: hatching in the default register, pins in calorie, a barcode strip in trax, a row of dots in ambient. Use it once per page.",
|
|
1351
1708
|
"code": "<Divider label=\"Body\" weight=\"band\" />"
|
|
1352
1709
|
}
|
|
1353
1710
|
]
|
|
@@ -1548,20 +1905,44 @@
|
|
|
1548
1905
|
},
|
|
1549
1906
|
"level": {
|
|
1550
1907
|
"kind": "enum",
|
|
1551
|
-
"description": "Outline depth, rendered as the matching h element. 1 is the page title and there should be one per page.",
|
|
1908
|
+
"description": "Outline depth, rendered as the matching h element. 1 is the page title and there should be one per page. 5 and 6 take the level-4 voice unless size says otherwise.",
|
|
1552
1909
|
"values": [
|
|
1553
1910
|
"1",
|
|
1554
1911
|
"2",
|
|
1555
1912
|
"3",
|
|
1556
|
-
"4"
|
|
1913
|
+
"4",
|
|
1914
|
+
"5",
|
|
1915
|
+
"6"
|
|
1557
1916
|
],
|
|
1558
1917
|
"default": "2"
|
|
1918
|
+
},
|
|
1919
|
+
"size": {
|
|
1920
|
+
"kind": "enum",
|
|
1921
|
+
"description": "The type voice, from 1 (display) to 4 (smallest), when it should differ from the level. Use it when a heading's place in the outline and its visual weight disagree, such as a level-2 title inside a dense card that should read small.",
|
|
1922
|
+
"values": [
|
|
1923
|
+
"1",
|
|
1924
|
+
"2",
|
|
1925
|
+
"3",
|
|
1926
|
+
"4"
|
|
1927
|
+
]
|
|
1559
1928
|
}
|
|
1560
1929
|
},
|
|
1561
1930
|
"state": {
|
|
1562
1931
|
"level": {
|
|
1563
1932
|
"description": "The outline depth, and so the type voice in use.",
|
|
1564
1933
|
"attribute": "data-sprint-level",
|
|
1934
|
+
"values": [
|
|
1935
|
+
"1",
|
|
1936
|
+
"2",
|
|
1937
|
+
"3",
|
|
1938
|
+
"4",
|
|
1939
|
+
"5",
|
|
1940
|
+
"6"
|
|
1941
|
+
]
|
|
1942
|
+
},
|
|
1943
|
+
"size": {
|
|
1944
|
+
"description": "The type voice, present only when it differs from the level.",
|
|
1945
|
+
"attribute": "data-sprint-size",
|
|
1565
1946
|
"values": [
|
|
1566
1947
|
"1",
|
|
1567
1948
|
"2",
|
|
@@ -1582,6 +1963,11 @@
|
|
|
1582
1963
|
"title": "A section title",
|
|
1583
1964
|
"description": "The default level, for a region inside a page.",
|
|
1584
1965
|
"code": "<Heading>Every variant</Heading>"
|
|
1966
|
+
},
|
|
1967
|
+
{
|
|
1968
|
+
"title": "A deep heading in a small voice",
|
|
1969
|
+
"description": "The outline needs a level-3 heading, but inside a compact card it should read at the smallest size.",
|
|
1970
|
+
"code": "<Heading level={3} size={4}>Sunday speakers</Heading>"
|
|
1585
1971
|
}
|
|
1586
1972
|
]
|
|
1587
1973
|
},
|
|
@@ -1696,19 +2082,51 @@
|
|
|
1696
2082
|
]
|
|
1697
2083
|
},
|
|
1698
2084
|
{
|
|
1699
|
-
"name": "
|
|
1700
|
-
"category": "
|
|
1701
|
-
"summary": "A
|
|
1702
|
-
"whenToUse": "Use
|
|
1703
|
-
"whenNotToUse": "Do not use
|
|
2085
|
+
"name": "Kbd",
|
|
2086
|
+
"category": "typography",
|
|
2087
|
+
"summary": "A keyboard key or shortcut, drawn as key caps. Pass the combination as text joined with plus signs.",
|
|
2088
|
+
"whenToUse": "Use to tell a person which keys do something: an undo hint in a toast, a shortcut beside a menu item's description, a search field's focus key. Write the combination as one string, such as \"Ctrl+Z\" or \"Shift+Enter\"; each part becomes its own cap.",
|
|
2089
|
+
"whenNotToUse": "Do not use for code or for a value someone types into a field; that is code or a CodeBlock. Do not use it as a control; it does not press anything.",
|
|
1704
2090
|
"status": "experimental",
|
|
1705
2091
|
"props": {
|
|
1706
|
-
"
|
|
2092
|
+
"children": {
|
|
1707
2093
|
"kind": "string",
|
|
1708
|
-
"description": "The
|
|
2094
|
+
"description": "The keys, joined with \"+\", such as \"Ctrl+Z\". A lone \"+\" is a key in its own right.",
|
|
1709
2095
|
"required": true
|
|
1710
|
-
}
|
|
1711
|
-
|
|
2096
|
+
}
|
|
2097
|
+
},
|
|
2098
|
+
"agentView": {
|
|
2099
|
+
"example": "- **Kbd** \"Ctrl+Z\""
|
|
2100
|
+
},
|
|
2101
|
+
"examples": [
|
|
2102
|
+
{
|
|
2103
|
+
"title": "An undo shortcut",
|
|
2104
|
+
"code": "<Kbd>Ctrl+Z</Kbd>"
|
|
2105
|
+
},
|
|
2106
|
+
{
|
|
2107
|
+
"title": "A single key",
|
|
2108
|
+
"description": "One key is one cap.",
|
|
2109
|
+
"code": "<Kbd>/</Kbd>"
|
|
2110
|
+
}
|
|
2111
|
+
],
|
|
2112
|
+
"a11y": {
|
|
2113
|
+
"notes": "A kbd element nesting one kbd per key, which is the HTML pattern for a key combination. The plus signs between caps are hidden from assistive technology, which reads the keys in order."
|
|
2114
|
+
}
|
|
2115
|
+
},
|
|
2116
|
+
{
|
|
2117
|
+
"name": "Link",
|
|
2118
|
+
"category": "navigation",
|
|
2119
|
+
"summary": "A navigation link. It publishes its destination as state, so an agent reading the page in text learns the URL rather than having to click to find out, and it renders a real anchor in both views.",
|
|
2120
|
+
"whenToUse": "Use it for anything that changes the address: a nav item, a cross-reference, a link out to a specification. Set active on the item matching the current route so the agent view and the human view agree about where you are. Under a client-side router, pass an onClick that prevents default and navigates; the handler rides the anchor in both views, so agent clicks and open tools go through the router too.",
|
|
2121
|
+
"whenNotToUse": "Do not use it for an action that stays on the page; that is a Button. Do not register a tool on ordinary navigation: an agent can already reach a URL, and a page of links would flood its tool list for no gain.",
|
|
2122
|
+
"status": "experimental",
|
|
2123
|
+
"props": {
|
|
2124
|
+
"href": {
|
|
2125
|
+
"kind": "string",
|
|
2126
|
+
"description": "The destination. Published as data-sprint-href and carried in the agent view, so the URL is readable without a click.",
|
|
2127
|
+
"required": true
|
|
2128
|
+
},
|
|
2129
|
+
"children": {
|
|
1712
2130
|
"kind": "node",
|
|
1713
2131
|
"description": "The link text. It names the destination, so prefer the page's name over here or read more.",
|
|
1714
2132
|
"required": true
|
|
@@ -1897,6 +2315,146 @@
|
|
|
1897
2315
|
"notes": "A real ul or ol named by its label, with an explicit list role because the custom markers require list-style none and Safari would otherwise drop the list semantics. The item count is announced, and the markers are drawn as pseudo-elements, so a marker never carries meaning a screen reader would miss."
|
|
1898
2316
|
}
|
|
1899
2317
|
},
|
|
2318
|
+
{
|
|
2319
|
+
"name": "Menu",
|
|
2320
|
+
"category": "action",
|
|
2321
|
+
"summary": "A button that opens a short list of actions, links, or one-of-several choices. Registers one choose tool enumerating the items an agent can run.",
|
|
2322
|
+
"whenToUse": "Use to gather secondary actions behind one control: a card's edit, duplicate and delete; an account menu; a share menu; a switcher between a few named views. Items hold data, not elements: each is { label, onSelect?, href?, checked?, tone?, disabled?, group?, icon? }. An item with checked becomes a radio choice with a visible mark, so a menu can also pick one value from a short list.",
|
|
2323
|
+
"whenNotToUse": "Do not use for the primary action of a view; that is a Button. Do not use to pick a form value that is submitted with other fields; that is a Select or a SegmentedControl. Do not use for navigation that should stay visible; that is a Nav or a Breadcrumb. Items take plain text labels and an optional icon, never components.",
|
|
2324
|
+
"status": "experimental",
|
|
2325
|
+
"props": {
|
|
2326
|
+
"label": {
|
|
2327
|
+
"kind": "string",
|
|
2328
|
+
"description": "What the menu holds, such as \"Card actions\" or \"Share\". It is the trigger's text, names the menu for assistive technology, and derives the choose tool name.",
|
|
2329
|
+
"required": true
|
|
2330
|
+
},
|
|
2331
|
+
"items": {
|
|
2332
|
+
"kind": "array",
|
|
2333
|
+
"description": "The items in order: { label, onSelect?, href?, external?, checked?, tone?, disabled?, group?, icon? }. onSelect runs the item; href makes it a link, which agents reach by URL rather than the tool. checked, true or false, makes the item a radio choice. tone=\"danger\" marks a destructive item. Consecutive items sharing a group string are gathered under that heading.",
|
|
2334
|
+
"required": true
|
|
2335
|
+
},
|
|
2336
|
+
"icon": {
|
|
2337
|
+
"kind": "node",
|
|
2338
|
+
"description": "An icon drawn on the trigger before its label."
|
|
2339
|
+
},
|
|
2340
|
+
"hideLabel": {
|
|
2341
|
+
"kind": "boolean",
|
|
2342
|
+
"description": "Show only the icon on a square trigger, such as a three-dot card menu. The label stays the accessible name and the tool name, and shows as a tooltip. Requires icon.",
|
|
2343
|
+
"default": false
|
|
2344
|
+
},
|
|
2345
|
+
"size": {
|
|
2346
|
+
"kind": "enum",
|
|
2347
|
+
"description": "The trigger's size, matching Button.",
|
|
2348
|
+
"values": [
|
|
2349
|
+
"medium",
|
|
2350
|
+
"small"
|
|
2351
|
+
],
|
|
2352
|
+
"default": "medium"
|
|
2353
|
+
},
|
|
2354
|
+
"align": {
|
|
2355
|
+
"kind": "enum",
|
|
2356
|
+
"description": "Which edge of the trigger the list lines up with. Use end for a menu at the right of a card or a toolbar.",
|
|
2357
|
+
"values": [
|
|
2358
|
+
"start",
|
|
2359
|
+
"center",
|
|
2360
|
+
"end"
|
|
2361
|
+
],
|
|
2362
|
+
"default": "start"
|
|
2363
|
+
},
|
|
2364
|
+
"side": {
|
|
2365
|
+
"kind": "enum",
|
|
2366
|
+
"description": "Where the list prefers to open. It flips to the other side when there is no room, so a menu low on the screen still opens fully in view.",
|
|
2367
|
+
"values": [
|
|
2368
|
+
"below",
|
|
2369
|
+
"above"
|
|
2370
|
+
],
|
|
2371
|
+
"default": "below"
|
|
2372
|
+
},
|
|
2373
|
+
"disabled": {
|
|
2374
|
+
"kind": "boolean",
|
|
2375
|
+
"description": "Disable the trigger and unregister the choose tool.",
|
|
2376
|
+
"default": false
|
|
2377
|
+
},
|
|
2378
|
+
"onOpenChange": {
|
|
2379
|
+
"kind": "handler",
|
|
2380
|
+
"description": "Called with true when the list opens and false when it closes."
|
|
2381
|
+
},
|
|
2382
|
+
"agentName": {
|
|
2383
|
+
"kind": "string",
|
|
2384
|
+
"description": "Override the label used to derive the tool name, such as when every card on a page has a menu called Actions."
|
|
2385
|
+
},
|
|
2386
|
+
"agentTool": {
|
|
2387
|
+
"kind": "boolean",
|
|
2388
|
+
"description": "Set false to render the menu without registering the choose tool.",
|
|
2389
|
+
"default": true
|
|
2390
|
+
}
|
|
2391
|
+
},
|
|
2392
|
+
"state": {
|
|
2393
|
+
"open": {
|
|
2394
|
+
"description": "Present while the list is open on screen.",
|
|
2395
|
+
"attribute": "data-sprint-open"
|
|
2396
|
+
},
|
|
2397
|
+
"disabled": {
|
|
2398
|
+
"description": "Present when the menu cannot be opened.",
|
|
2399
|
+
"attribute": "data-sprint-disabled"
|
|
2400
|
+
}
|
|
2401
|
+
},
|
|
2402
|
+
"tools": {
|
|
2403
|
+
"choose": {
|
|
2404
|
+
"verb": "choose",
|
|
2405
|
+
"description": "Choose one item from this menu by its visible label, exactly as a person opening the menu and pressing the item would. The menu does not need to be open. Items that are links are not offered; reach them by their href. Returns the menu's state after the choice, so a follow-up read is usually unnecessary.",
|
|
2406
|
+
"inputSchema": {
|
|
2407
|
+
"type": "object",
|
|
2408
|
+
"properties": {
|
|
2409
|
+
"item": {
|
|
2410
|
+
"type": "string",
|
|
2411
|
+
"description": "The visible label of the item to choose."
|
|
2412
|
+
}
|
|
2413
|
+
},
|
|
2414
|
+
"required": [
|
|
2415
|
+
"item"
|
|
2416
|
+
]
|
|
2417
|
+
},
|
|
2418
|
+
"readOnly": false,
|
|
2419
|
+
"untrustedContent": true,
|
|
2420
|
+
"registeredWhen": "The menu is mounted and enabled, has at least one enabled item without an href, and no other component claims the same tool name. The registered schema enumerates those items' labels.",
|
|
2421
|
+
"unregisteredWhen": "The menu unmounts, becomes disabled, or its last enabled item without an href is removed or disabled."
|
|
2422
|
+
}
|
|
2423
|
+
},
|
|
2424
|
+
"agentView": {
|
|
2425
|
+
"example": "- **Menu** \"Card actions\" → tool `choose-card-actions`\n - part `item` \"Edit\"\n - part `item` \"Delete\" [tone=danger]"
|
|
2426
|
+
},
|
|
2427
|
+
"examples": [
|
|
2428
|
+
{
|
|
2429
|
+
"title": "Card actions",
|
|
2430
|
+
"description": "A three-dot trigger at the end of a card. The destructive item is marked, and an agent runs either item through the choose tool without opening anything.",
|
|
2431
|
+
"code": "<Menu\n label=\"Card actions\"\n icon={<MoreIcon />}\n hideLabel\n size=\"small\"\n align=\"end\"\n items={[\n { label: \"Edit\", onSelect: edit },\n { label: \"Duplicate\", onSelect: duplicate },\n { label: \"Delete\", tone: \"danger\", onSelect: remove },\n ]}\n/>"
|
|
2432
|
+
},
|
|
2433
|
+
{
|
|
2434
|
+
"title": "Links and actions together",
|
|
2435
|
+
"description": "An item with href is a real link and is left out of the tool, because a URL already reaches it. Groups gather related items under a heading.",
|
|
2436
|
+
"code": "<Menu\n label=\"Account\"\n items={[\n { label: \"Profile\", href: \"#/profile\", group: \"Signed in as Nomad\" },\n { label: \"Settings\", href: \"#/settings\", group: \"Signed in as Nomad\" },\n { label: \"Sign out\", onSelect: signOut },\n ]}\n/>"
|
|
2437
|
+
},
|
|
2438
|
+
{
|
|
2439
|
+
"title": "Choosing one of several",
|
|
2440
|
+
"description": "checked turns items into radio choices with a visible mark, so the menu doubles as a compact picker. It opens with focus on the checked item.",
|
|
2441
|
+
"code": "<Menu\n label={plannerLabel}\n agentName=\"Planner\"\n items={planners.map((name) => ({\n label: name,\n checked: name === planner,\n onSelect: () => setPlanner(name),\n }))}\n/>"
|
|
2442
|
+
}
|
|
2443
|
+
],
|
|
2444
|
+
"a11y": {
|
|
2445
|
+
"role": "menu",
|
|
2446
|
+
"keyboard": [
|
|
2447
|
+
"Enter, Space or Down Arrow on the trigger opens the list on the first or checked item",
|
|
2448
|
+
"Up Arrow on the trigger opens it on the last item",
|
|
2449
|
+
"Arrow keys move between items and wrap",
|
|
2450
|
+
"Home and End move to the first and last item",
|
|
2451
|
+
"A letter moves to the next item starting with it",
|
|
2452
|
+
"Escape closes the list and returns focus to the trigger",
|
|
2453
|
+
"Tab closes the list and moves on"
|
|
2454
|
+
],
|
|
2455
|
+
"notes": "The trigger has aria-haspopup=menu and aria-expanded. Items are menuitem buttons or links, or menuitemradio with aria-checked. The list is a popover in the top layer rendered inside the menu's own DOM, so it opens above a modal Dialog and is never clipped by an ancestor's overflow. Disabled items are skipped by the keyboard."
|
|
2456
|
+
}
|
|
2457
|
+
},
|
|
1900
2458
|
{
|
|
1901
2459
|
"name": "MetaLine",
|
|
1902
2460
|
"category": "display",
|
|
@@ -2396,6 +2954,22 @@
|
|
|
2396
2954
|
"kind": "number",
|
|
2397
2955
|
"description": "The value at which the work is complete.",
|
|
2398
2956
|
"default": 100
|
|
2957
|
+
},
|
|
2958
|
+
"tone": {
|
|
2959
|
+
"kind": "enum",
|
|
2960
|
+
"values": [
|
|
2961
|
+
"info",
|
|
2962
|
+
"action",
|
|
2963
|
+
"warning",
|
|
2964
|
+
"danger"
|
|
2965
|
+
],
|
|
2966
|
+
"description": "The fill colour. info is the default for ordinary loading; action suits a goal being worked towards; warning and danger mark a bar running out or over a limit.",
|
|
2967
|
+
"default": "info"
|
|
2968
|
+
},
|
|
2969
|
+
"hideLabel": {
|
|
2970
|
+
"kind": "boolean",
|
|
2971
|
+
"description": "Hide the label and percentage visually while keeping the label as the bar's accessible name and agent label. Use when a nearby heading already says what the bar measures.",
|
|
2972
|
+
"default": false
|
|
2399
2973
|
}
|
|
2400
2974
|
},
|
|
2401
2975
|
"state": {
|
|
@@ -2406,6 +2980,10 @@
|
|
|
2406
2980
|
"value": {
|
|
2407
2981
|
"description": "The fraction complete as a whole percentage, such as 40%. Absent while indeterminate.",
|
|
2408
2982
|
"attribute": "data-sprint-value"
|
|
2983
|
+
},
|
|
2984
|
+
"tone": {
|
|
2985
|
+
"description": "The fill tone, when it is not the default info.",
|
|
2986
|
+
"attribute": "data-sprint-tone"
|
|
2409
2987
|
}
|
|
2410
2988
|
},
|
|
2411
2989
|
"agentView": {
|
|
@@ -2426,6 +3004,11 @@
|
|
|
2426
3004
|
"title": "Complete",
|
|
2427
3005
|
"description": "At max the loading state clears, so the line reads as finished rather than stalled.",
|
|
2428
3006
|
"code": "<Progress label=\"Importing manifest\" value={240} max={240} />"
|
|
3007
|
+
},
|
|
3008
|
+
{
|
|
3009
|
+
"title": "A bare goal bar",
|
|
3010
|
+
"description": "A heading beside the bar already names it, so the label is hidden and only the bar shows, in the action tone.",
|
|
3011
|
+
"code": "<Progress label=\"Notes this week\" value={3} max={5} tone=\"action\" hideLabel />"
|
|
2429
3012
|
}
|
|
2430
3013
|
],
|
|
2431
3014
|
"a11y": {
|
|
@@ -2434,133 +3017,319 @@
|
|
|
2434
3017
|
}
|
|
2435
3018
|
},
|
|
2436
3019
|
{
|
|
2437
|
-
"name": "
|
|
3020
|
+
"name": "Prose",
|
|
3021
|
+
"category": "typography",
|
|
3022
|
+
"summary": "Typography for long-form rendered content such as Markdown: headings, paragraphs, lists, links, code, tables and quotes, in the theme's voice.",
|
|
3023
|
+
"whenToUse": "Wrap HTML you did not lay out yourself: member notes rendered from Markdown, an agenda's program details, a help article. Pass the original Markdown as source and the agent view carries it verbatim, which is better for an agent than flattened text. Sprint does not parse Markdown; render it with the library of your choice and pass the result as children.",
|
|
3024
|
+
"whenNotToUse": "Do not use for interface copy you write yourself; that is Text and Heading. Do not put interactive Sprint components inside it; Prose styles plain elements.",
|
|
3025
|
+
"status": "experimental",
|
|
3026
|
+
"props": {
|
|
3027
|
+
"children": {
|
|
3028
|
+
"kind": "node",
|
|
3029
|
+
"description": "The rendered content: plain HTML elements such as p, ul, a, code and table.",
|
|
3030
|
+
"required": true
|
|
3031
|
+
},
|
|
3032
|
+
"source": {
|
|
3033
|
+
"kind": "string",
|
|
3034
|
+
"description": "The Markdown the children were rendered from. The agent view shows this instead of the flattened text, keeping lists, links and emphasis intact."
|
|
3035
|
+
},
|
|
3036
|
+
"label": {
|
|
3037
|
+
"kind": "string",
|
|
3038
|
+
"description": "What the content is, such as \"Member notes\". Makes the block a labelled region and names it in the agent view."
|
|
3039
|
+
},
|
|
3040
|
+
"size": {
|
|
3041
|
+
"kind": "enum",
|
|
3042
|
+
"description": "small for notes inside a card or a side panel.",
|
|
3043
|
+
"values": [
|
|
3044
|
+
"small",
|
|
3045
|
+
"normal"
|
|
3046
|
+
],
|
|
3047
|
+
"default": "normal"
|
|
3048
|
+
}
|
|
3049
|
+
},
|
|
3050
|
+
"state": {
|
|
3051
|
+
"size": {
|
|
3052
|
+
"description": "Present as small for the compact size.",
|
|
3053
|
+
"attribute": "data-sprint-size",
|
|
3054
|
+
"values": [
|
|
3055
|
+
"small"
|
|
3056
|
+
]
|
|
3057
|
+
}
|
|
3058
|
+
},
|
|
3059
|
+
"agentView": {
|
|
3060
|
+
"example": "- **Prose** \"Member notes\"\n - part `content` \"Moved in **March**. Plays the organ.\""
|
|
3061
|
+
},
|
|
3062
|
+
"examples": [
|
|
3063
|
+
{
|
|
3064
|
+
"title": "Rendered Markdown",
|
|
3065
|
+
"description": "The page renders Markdown however it likes and passes the original as source, which the agent view carries unchanged.",
|
|
3066
|
+
"code": "<Prose label=\"Member notes\" source={notes}>\n <Markdown>{notes}</Markdown>\n</Prose>"
|
|
3067
|
+
},
|
|
3068
|
+
{
|
|
3069
|
+
"title": "Compact notes",
|
|
3070
|
+
"code": "<Prose size=\"small\">\n <p>Prefers <strong>text</strong> after six.</p>\n <ul>\n <li>Organ</li>\n <li>Youth program</li>\n </ul>\n</Prose>"
|
|
3071
|
+
}
|
|
3072
|
+
],
|
|
3073
|
+
"a11y": {
|
|
3074
|
+
"role": "region",
|
|
3075
|
+
"notes": "With a label the block is a named region; without one it adds no semantics of its own. The content keeps its own elements, so headings join the page outline."
|
|
3076
|
+
}
|
|
3077
|
+
},
|
|
3078
|
+
{
|
|
3079
|
+
"name": "RadioGroup",
|
|
2438
3080
|
"category": "input",
|
|
2439
|
-
"summary": "
|
|
2440
|
-
"whenToUse": "Use
|
|
2441
|
-
"whenNotToUse": "Do not use
|
|
3081
|
+
"summary": "One choice from a short list of options, each a native radio button with a label and an optional line of description. Registers one select tool enumerating the options.",
|
|
3082
|
+
"whenToUse": "Use when the options need explaining: a role with what it can do, a plan with what it includes, a delivery speed with its cost. Options hold data: { value, label, description?, disabled? }. The group is a fieldset whose legend is the label, so it submits under one name inside a form.",
|
|
3083
|
+
"whenNotToUse": "Do not use for two to four short labels that need no description; that is a SegmentedControl. Do not use for a long list; that is a Select. Do not use for choosing several; that is a set of Checkboxes. Descriptions are plain strings, not components.",
|
|
2442
3084
|
"status": "experimental",
|
|
2443
3085
|
"props": {
|
|
2444
3086
|
"label": {
|
|
2445
3087
|
"kind": "string",
|
|
2446
|
-
"description": "
|
|
3088
|
+
"description": "The question the options answer, such as \"Role\". Rendered as the legend and used to derive the tool name.",
|
|
3089
|
+
"required": true
|
|
3090
|
+
},
|
|
3091
|
+
"options": {
|
|
3092
|
+
"kind": "array",
|
|
3093
|
+
"description": "The options in order: { value, label, description?, disabled? }. label is what a person reads and what the select tool accepts. description is a sentence under the label, linked to its radio with aria-describedby and carried into the agent view as part state.",
|
|
2447
3094
|
"required": true
|
|
2448
3095
|
},
|
|
2449
3096
|
"value": {
|
|
2450
3097
|
"kind": "string",
|
|
2451
|
-
"description": "The
|
|
3098
|
+
"description": "The selected option's value, or an empty string for none. Fully controlled.",
|
|
2452
3099
|
"required": true
|
|
2453
3100
|
},
|
|
2454
3101
|
"onChange": {
|
|
2455
3102
|
"kind": "handler",
|
|
2456
|
-
"description": "Called with the
|
|
3103
|
+
"description": "Called with the value a person or an agent selects.",
|
|
2457
3104
|
"required": true
|
|
2458
3105
|
},
|
|
2459
|
-
"
|
|
2460
|
-
"kind": "handler",
|
|
2461
|
-
"description": "Called with the query when Enter is pressed or the search tool runs. Leave it off for a live filter that reacts to onChange alone."
|
|
2462
|
-
},
|
|
2463
|
-
"placeholder": {
|
|
3106
|
+
"hint": {
|
|
2464
3107
|
"kind": "string",
|
|
2465
|
-
"description": "
|
|
2466
|
-
},
|
|
2467
|
-
"hideLabel": {
|
|
2468
|
-
"kind": "boolean",
|
|
2469
|
-
"description": "Hide the label visually while keeping it for screen readers and agents. Pair it with a placeholder so sighted people still know what the field searches.",
|
|
2470
|
-
"default": false
|
|
3108
|
+
"description": "Guidance under the group. Replaced by error while one is set."
|
|
2471
3109
|
},
|
|
2472
|
-
"
|
|
3110
|
+
"error": {
|
|
2473
3111
|
"kind": "string",
|
|
2474
|
-
"description": "A
|
|
3112
|
+
"description": "A validation message that marks the group invalid."
|
|
2475
3113
|
},
|
|
2476
3114
|
"name": {
|
|
2477
3115
|
"kind": "string",
|
|
2478
|
-
"description": "The native form name
|
|
3116
|
+
"description": "The native form name the selected value submits under."
|
|
2479
3117
|
},
|
|
2480
3118
|
"disabled": {
|
|
2481
3119
|
"kind": "boolean",
|
|
2482
|
-
"description": "Disable
|
|
3120
|
+
"description": "Disable every option and unregister the select tool.",
|
|
3121
|
+
"default": false
|
|
3122
|
+
},
|
|
3123
|
+
"required": {
|
|
3124
|
+
"kind": "boolean",
|
|
3125
|
+
"description": "Mark the group as needing an answer.",
|
|
2483
3126
|
"default": false
|
|
2484
3127
|
},
|
|
2485
3128
|
"agentName": {
|
|
2486
3129
|
"kind": "string",
|
|
2487
|
-
"description": "Override the label used to derive the tool name
|
|
3130
|
+
"description": "Override the label used to derive the tool name."
|
|
2488
3131
|
},
|
|
2489
3132
|
"agentTool": {
|
|
2490
3133
|
"kind": "boolean",
|
|
2491
|
-
"description": "Set false to render the
|
|
3134
|
+
"description": "Set false to render the group without registering the select tool.",
|
|
2492
3135
|
"default": true
|
|
2493
3136
|
}
|
|
2494
3137
|
},
|
|
2495
3138
|
"state": {
|
|
2496
3139
|
"value": {
|
|
2497
|
-
"description": "The
|
|
3140
|
+
"description": "The selected option's label.",
|
|
2498
3141
|
"attribute": "data-sprint-value"
|
|
2499
3142
|
},
|
|
2500
3143
|
"empty": {
|
|
2501
|
-
"description": "Present while
|
|
3144
|
+
"description": "Present while nothing is selected.",
|
|
2502
3145
|
"attribute": "data-sprint-empty"
|
|
2503
3146
|
},
|
|
2504
|
-
"shortcut": {
|
|
2505
|
-
"description": "The key that focuses the field from anywhere on the page.",
|
|
2506
|
-
"attribute": "data-sprint-shortcut"
|
|
2507
|
-
},
|
|
2508
3147
|
"disabled": {
|
|
2509
|
-
"description": "Present when the
|
|
3148
|
+
"description": "Present when the group cannot be changed.",
|
|
2510
3149
|
"attribute": "data-sprint-disabled"
|
|
3150
|
+
},
|
|
3151
|
+
"required": {
|
|
3152
|
+
"description": "Present when an answer is needed.",
|
|
3153
|
+
"attribute": "data-sprint-required"
|
|
3154
|
+
},
|
|
3155
|
+
"invalid": {
|
|
3156
|
+
"description": "Present while an error is set.",
|
|
3157
|
+
"attribute": "data-sprint-invalid"
|
|
2511
3158
|
}
|
|
2512
3159
|
},
|
|
2513
3160
|
"tools": {
|
|
2514
|
-
"
|
|
2515
|
-
"verb": "
|
|
2516
|
-
"description": "
|
|
3161
|
+
"select": {
|
|
3162
|
+
"verb": "select",
|
|
3163
|
+
"description": "Select one of this group's options by its visible label, exactly as a person clicking its radio button would. Only one option is selected at a time, so this replaces the current one. Returns the group's state after the change.",
|
|
2517
3164
|
"inputSchema": {
|
|
2518
3165
|
"type": "object",
|
|
2519
3166
|
"properties": {
|
|
2520
|
-
"
|
|
3167
|
+
"option": {
|
|
2521
3168
|
"type": "string",
|
|
2522
|
-
"description": "The
|
|
3169
|
+
"description": "The visible label of the option to select."
|
|
2523
3170
|
}
|
|
2524
3171
|
},
|
|
2525
3172
|
"required": [
|
|
2526
|
-
"
|
|
3173
|
+
"option"
|
|
2527
3174
|
]
|
|
2528
3175
|
},
|
|
2529
3176
|
"readOnly": false,
|
|
2530
3177
|
"untrustedContent": true,
|
|
2531
|
-
"registeredWhen": "The
|
|
2532
|
-
"unregisteredWhen": "The
|
|
3178
|
+
"registeredWhen": "The group is mounted and enabled, has at least one enabled option, and no other component claims the same tool name. The registered schema enumerates the enabled options' labels.",
|
|
3179
|
+
"unregisteredWhen": "The group unmounts, becomes disabled, or loses its last enabled option."
|
|
2533
3180
|
}
|
|
2534
3181
|
},
|
|
2535
3182
|
"agentView": {
|
|
2536
|
-
"example": "- **
|
|
3183
|
+
"example": "- **RadioGroup** \"Role\" [value=Viewer] → tool `select-role`\n - part `option` \"Viewer\" [checked, description=Sees the board]\n - part `option` \"Editor\" [description=Changes callings]"
|
|
2537
3184
|
},
|
|
2538
3185
|
"examples": [
|
|
2539
3186
|
{
|
|
2540
|
-
"title": "
|
|
2541
|
-
"description": "
|
|
2542
|
-
"code": "<
|
|
2543
|
-
},
|
|
2544
|
-
{
|
|
2545
|
-
"title": "A slash shortcut",
|
|
2546
|
-
"description": "Pressing / anywhere on the page focuses the field unless focus is already in something editable. The key chip disappears once the field has focus or a query.",
|
|
2547
|
-
"code": "<SearchField\n label=\"Users\"\n value={query}\n onChange={setQuery}\n shortcut=\"/\"\n placeholder=\"Name or email\"\n/>"
|
|
3187
|
+
"title": "Options that need explaining",
|
|
3188
|
+
"description": "Each role says what it allows. The descriptions reach screen readers through aria-describedby and agents through part state.",
|
|
3189
|
+
"code": "<RadioGroup\n label=\"Role\"\n value={role}\n onChange={setRole}\n options={[\n { value: \"viewer\", label: \"Viewer\", description: \"Sees the board and the agenda.\" },\n { value: \"editor\", label: \"Editor\", description: \"Moves people between callings.\" },\n { value: \"admin\", label: \"Admin\", description: \"Also invites and removes people.\" },\n ]}\n/>"
|
|
2548
3190
|
},
|
|
2549
3191
|
{
|
|
2550
|
-
"title": "
|
|
2551
|
-
"
|
|
2552
|
-
"code": "<SearchField\n label=\"Flight logs\"\n value={query}\n onChange={setQuery}\n onSubmit={runSearch}\n placeholder=\"Callsign or tail number\"\n/>"
|
|
3192
|
+
"title": "A required choice with an unavailable option",
|
|
3193
|
+
"code": "<RadioGroup\n label=\"Delivery\"\n required\n value={delivery}\n onChange={setDelivery}\n error={delivery === \"\" ? \"Choose how to send the invite.\" : undefined}\n options={[\n { value: \"email\", label: \"Email\" },\n { value: \"text\", label: \"Text message\", disabled: true, description: \"No phone number on file.\" },\n ]}\n/>"
|
|
2553
3194
|
}
|
|
2554
3195
|
],
|
|
2555
3196
|
"a11y": {
|
|
2556
|
-
"role": "
|
|
3197
|
+
"role": "radiogroup",
|
|
2557
3198
|
"keyboard": [
|
|
2558
|
-
"
|
|
2559
|
-
"
|
|
2560
|
-
"The shortcut key, when set, focuses the field from anywhere on the page"
|
|
3199
|
+
"Arrow keys move between options and select",
|
|
3200
|
+
"Tab enters and leaves the group"
|
|
2561
3201
|
],
|
|
2562
|
-
"notes": "
|
|
2563
|
-
}
|
|
3202
|
+
"notes": "A fieldset of native radio inputs sharing one name, so the browser supplies arrow-key movement and form submission. The legend names the group; a description is linked to its own radio."
|
|
3203
|
+
}
|
|
3204
|
+
},
|
|
3205
|
+
{
|
|
3206
|
+
"name": "SearchField",
|
|
3207
|
+
"category": "input",
|
|
3208
|
+
"summary": "A search box inside its own search landmark: a labelled query field with a clear control, Escape to clear, Enter to submit, and an optional page-wide key that focuses it. It registers one search tool that sets the query and submits it.",
|
|
3209
|
+
"whenToUse": "Use it to filter or search a collection on the page, such as narrowing a users list by name. The landmark lets screen reader users jump straight to it, and the optional shortcut gives keyboard users the familiar slash-to-search.",
|
|
3210
|
+
"whenNotToUse": "Do not use it for any other single-line value; a name, an email address, or an identifier is a TextInput. Do not use it to choose from a short known set, which is a Select or a SegmentedControl. Do not give two fields on one page the same shortcut.",
|
|
3211
|
+
"status": "experimental",
|
|
3212
|
+
"props": {
|
|
3213
|
+
"label": {
|
|
3214
|
+
"kind": "string",
|
|
3215
|
+
"description": "What is being searched, as a noun phrase such as \"Users\". Names the landmark and the field, and derives the tool name, so \"Users\" becomes search-users.",
|
|
3216
|
+
"required": true
|
|
3217
|
+
},
|
|
3218
|
+
"value": {
|
|
3219
|
+
"kind": "string",
|
|
3220
|
+
"description": "The current query. The field is fully controlled.",
|
|
3221
|
+
"required": true
|
|
3222
|
+
},
|
|
3223
|
+
"onChange": {
|
|
3224
|
+
"kind": "handler",
|
|
3225
|
+
"description": "Called with the new query on every change, including the clear control, Escape, and the search tool.",
|
|
3226
|
+
"required": true
|
|
3227
|
+
},
|
|
3228
|
+
"onSubmit": {
|
|
3229
|
+
"kind": "handler",
|
|
3230
|
+
"description": "Called with the query when Enter is pressed or the search tool runs. Leave it off for a live filter that reacts to onChange alone."
|
|
3231
|
+
},
|
|
3232
|
+
"placeholder": {
|
|
3233
|
+
"kind": "string",
|
|
3234
|
+
"description": "Ghost text shown while the field is empty, e.g. \"Name or email\"."
|
|
3235
|
+
},
|
|
3236
|
+
"hideLabel": {
|
|
3237
|
+
"kind": "boolean",
|
|
3238
|
+
"description": "Hide the label visually while keeping it for screen readers and agents. Pair it with a placeholder so sighted people still know what the field searches.",
|
|
3239
|
+
"default": false
|
|
3240
|
+
},
|
|
3241
|
+
"shortcut": {
|
|
3242
|
+
"kind": "string",
|
|
3243
|
+
"description": "A single key that focuses the field from anywhere on the page, shown as a key chip while the field is empty and unfocused. Ignored while focus is in another editable element or a modifier is held. Pass \"/\" for the common convention; omit or pass false for none."
|
|
3244
|
+
},
|
|
3245
|
+
"name": {
|
|
3246
|
+
"kind": "string",
|
|
3247
|
+
"description": "The native form name of the query input."
|
|
3248
|
+
},
|
|
3249
|
+
"disabled": {
|
|
3250
|
+
"kind": "boolean",
|
|
3251
|
+
"description": "Disable the field, its shortcut, and its search tool.",
|
|
3252
|
+
"default": false
|
|
3253
|
+
},
|
|
3254
|
+
"agentName": {
|
|
3255
|
+
"kind": "string",
|
|
3256
|
+
"description": "Override the label used to derive the tool name, when two search fields on a page would otherwise collide."
|
|
3257
|
+
},
|
|
3258
|
+
"agentTool": {
|
|
3259
|
+
"kind": "boolean",
|
|
3260
|
+
"description": "Set false to render the field without registering a search tool.",
|
|
3261
|
+
"default": true
|
|
3262
|
+
}
|
|
3263
|
+
},
|
|
3264
|
+
"state": {
|
|
3265
|
+
"value": {
|
|
3266
|
+
"description": "The current query, absent while the field is empty.",
|
|
3267
|
+
"attribute": "data-sprint-value"
|
|
3268
|
+
},
|
|
3269
|
+
"empty": {
|
|
3270
|
+
"description": "Present while the field holds no query.",
|
|
3271
|
+
"attribute": "data-sprint-empty"
|
|
3272
|
+
},
|
|
3273
|
+
"shortcut": {
|
|
3274
|
+
"description": "The key that focuses the field from anywhere on the page.",
|
|
3275
|
+
"attribute": "data-sprint-shortcut"
|
|
3276
|
+
},
|
|
3277
|
+
"disabled": {
|
|
3278
|
+
"description": "Present when the field cannot be edited.",
|
|
3279
|
+
"attribute": "data-sprint-disabled"
|
|
3280
|
+
}
|
|
3281
|
+
},
|
|
3282
|
+
"tools": {
|
|
3283
|
+
"search": {
|
|
3284
|
+
"verb": "search",
|
|
3285
|
+
"description": "Search with this field: replace its text with the query, exactly as a person typing it would, then submit it as pressing Enter would. Pass an empty string to clear the search. Results usually render elsewhere on the page; this returns the field's state after the search, so read the results region next.",
|
|
3286
|
+
"inputSchema": {
|
|
3287
|
+
"type": "object",
|
|
3288
|
+
"properties": {
|
|
3289
|
+
"query": {
|
|
3290
|
+
"type": "string",
|
|
3291
|
+
"description": "The full search text. Replaces the current query rather than appending to it; an empty string clears the search."
|
|
3292
|
+
}
|
|
3293
|
+
},
|
|
3294
|
+
"required": [
|
|
3295
|
+
"query"
|
|
3296
|
+
]
|
|
3297
|
+
},
|
|
3298
|
+
"readOnly": false,
|
|
3299
|
+
"untrustedContent": true,
|
|
3300
|
+
"registeredWhen": "The field is mounted, enabled, has a resolvable label, and no other component claims the same tool name.",
|
|
3301
|
+
"unregisteredWhen": "The field unmounts or becomes disabled."
|
|
3302
|
+
}
|
|
3303
|
+
},
|
|
3304
|
+
"agentView": {
|
|
3305
|
+
"example": "- **SearchField** \"Users\" [empty, shortcut=/] → tool `search-users`"
|
|
3306
|
+
},
|
|
3307
|
+
"examples": [
|
|
3308
|
+
{
|
|
3309
|
+
"title": "Filtering a list",
|
|
3310
|
+
"description": "A live filter: onChange narrows the list as the person types, so there is no onSubmit. The label is hidden and the placeholder says what can be matched.",
|
|
3311
|
+
"code": "<SearchField\n label=\"Users\"\n hideLabel\n value={query}\n onChange={setQuery}\n placeholder=\"Name or email\"\n/>"
|
|
3312
|
+
},
|
|
3313
|
+
{
|
|
3314
|
+
"title": "A slash shortcut",
|
|
3315
|
+
"description": "Pressing / anywhere on the page focuses the field unless focus is already in something editable. The key chip disappears once the field has focus or a query.",
|
|
3316
|
+
"code": "<SearchField\n label=\"Users\"\n value={query}\n onChange={setQuery}\n shortcut=\"/\"\n placeholder=\"Name or email\"\n/>"
|
|
3317
|
+
},
|
|
3318
|
+
{
|
|
3319
|
+
"title": "Submitting a query",
|
|
3320
|
+
"description": "For a search that is too costly to run on every keystroke, onSubmit receives the query on Enter and when the search tool runs.",
|
|
3321
|
+
"code": "<SearchField\n label=\"Flight logs\"\n value={query}\n onChange={setQuery}\n onSubmit={runSearch}\n placeholder=\"Callsign or tail number\"\n/>"
|
|
3322
|
+
}
|
|
3323
|
+
],
|
|
3324
|
+
"a11y": {
|
|
3325
|
+
"role": "search",
|
|
3326
|
+
"keyboard": [
|
|
3327
|
+
"Enter submits the query",
|
|
3328
|
+
"Escape clears a non-empty query and keeps focus; on an empty field it passes through, so an enclosing dialog can close",
|
|
3329
|
+
"The shortcut key, when set, focuses the field from anywhere on the page"
|
|
3330
|
+
],
|
|
3331
|
+
"notes": "The root is a form with role search, labelled by the field's label, so it is listed as a search landmark. The input is type search and announces its shortcut with aria-keyshortcuts; the key chip itself is hidden from assistive technology. The clear control appears only while there is a query, and returns focus to the field."
|
|
3332
|
+
},
|
|
2564
3333
|
"relatedComponents": [
|
|
2565
3334
|
"TextInput"
|
|
2566
3335
|
]
|
|
@@ -2636,7 +3405,7 @@
|
|
|
2636
3405
|
},
|
|
2637
3406
|
"options": {
|
|
2638
3407
|
"kind": "array",
|
|
2639
|
-
"description": "The choices in display order: { value, label, count? }. The label is what a person sees and what the select tool accepts, so an agent never has to know the value. count renders as a muted chip beside the label and reaches the agent view as part state, so never fold a count into the label.",
|
|
3408
|
+
"description": "The choices in display order: { value, label, count?, disabled? }. The label is what a person sees and what the select tool accepts, so an agent never has to know the value. count renders as a muted chip beside the label and reaches the agent view as part state, so never fold a count into the label. disabled keeps one option visible but unselectable: it is skipped by the arrow keys, left out of the tool's enum, and marked disabled in the agent view.",
|
|
2640
3409
|
"required": true
|
|
2641
3410
|
},
|
|
2642
3411
|
"value": {
|
|
@@ -2745,14 +3514,19 @@
|
|
|
2745
3514
|
"title": "A disabled control",
|
|
2746
3515
|
"description": "Disabled unregisters the tool, so an agent cannot select an option a person could not.",
|
|
2747
3516
|
"code": "<SegmentedControl\n label=\"Density\"\n disabled\n value=\"dense\"\n onChange={setDensity}\n options={[\n { value: \"dense\", label: \"dense\" },\n { value: \"roomy\", label: \"roomy\" },\n ]}\n/>"
|
|
3517
|
+
},
|
|
3518
|
+
{
|
|
3519
|
+
"title": "One option unavailable",
|
|
3520
|
+
"description": "A disabled option stays in place so the set of choices reads the same, but nobody can pick it: arrow keys skip it and the select tool does not offer it.",
|
|
3521
|
+
"code": "<SegmentedControl\n label=\"Interview\"\n value={status}\n onChange={setStatus}\n options={[\n { value: \"pending\", label: \"Pending\" },\n { value: \"accepted\", label: \"Accepted\", disabled: !interviewed },\n { value: \"declined\", label: \"Declined\", disabled: !interviewed },\n ]}\n/>"
|
|
2748
3522
|
}
|
|
2749
3523
|
],
|
|
2750
3524
|
"a11y": {
|
|
2751
3525
|
"role": "radiogroup",
|
|
2752
3526
|
"keyboard": [
|
|
2753
|
-
"Arrow keys move to the next or previous option and select it",
|
|
2754
|
-
"Home selects the first option",
|
|
2755
|
-
"End selects the last option",
|
|
3527
|
+
"Arrow keys move to the next or previous enabled option and select it",
|
|
3528
|
+
"Home selects the first enabled option",
|
|
3529
|
+
"End selects the last enabled option",
|
|
2756
3530
|
"Tab enters and leaves the group once"
|
|
2757
3531
|
],
|
|
2758
3532
|
"notes": "Roving tabindex: only the selected option is in the tab order. Selection follows focus, which is the expected behaviour for a radio group. An option with a count is named by its label and its count together; the hint is linked to the group with aria-describedby."
|
|
@@ -2763,7 +3537,7 @@
|
|
|
2763
3537
|
"category": "input",
|
|
2764
3538
|
"summary": "A dropdown of mutually exclusive options: a select-only combobox that opens a listbox on click or keyboard, carrying its own label, hint, and error. It registers a single select tool whose schema enumerates the option labels currently on offer.",
|
|
2765
3539
|
"whenToUse": "Use it when one value is chosen from a list too long to lay out flat: a region, a squad, a category. Options are data ({ value, label }), the tool accepts the visible label, and in agent view every option renders as its own control, so an agent picks one without opening anything.",
|
|
2766
|
-
"whenNotToUse": "Do not use it for two to four short options a person should compare at a glance; that is a SegmentedControl. Do not use it for an on/off state, which is a Checkbox or a Switch, and never for navigation.",
|
|
3540
|
+
"whenNotToUse": "Do not use it for two to four short options a person should compare at a glance; that is a SegmentedControl. Do not use it for a list long enough to need searching, grouping or custom option rendering; that is a Combobox. Do not use it for an on/off state, which is a Checkbox or a Switch, and never for navigation.",
|
|
2767
3541
|
"status": "experimental",
|
|
2768
3542
|
"props": {
|
|
2769
3543
|
"label": {
|
|
@@ -2965,6 +3739,14 @@
|
|
|
2965
3739
|
"kind": "handler",
|
|
2966
3740
|
"description": "Called with the collapsed state the Shell wants. Use it to remember the choice across visits."
|
|
2967
3741
|
},
|
|
3742
|
+
"drawerOpen": {
|
|
3743
|
+
"kind": "boolean",
|
|
3744
|
+
"description": "Whether the narrow-viewport drawer is open, when the owner keeps that state: a guided tour that steps into the sidebar, or a page that closes the drawer after an action. Pair it with onDrawerOpenChange."
|
|
3745
|
+
},
|
|
3746
|
+
"onDrawerOpenChange": {
|
|
3747
|
+
"kind": "handler",
|
|
3748
|
+
"description": "Called with the drawer state the Shell wants: true from the menu toggle, false from the close toggle or a link followed inside the drawer."
|
|
3749
|
+
},
|
|
2968
3750
|
"hideLabel": {
|
|
2969
3751
|
"kind": "string",
|
|
2970
3752
|
"description": "Label of the wide-viewport toggle while the sidebar is shown.",
|
|
@@ -3004,9 +3786,75 @@
|
|
|
3004
3786
|
"title": "A sidebar that can be hidden",
|
|
3005
3787
|
"description": "With collapsible, a person can put the sidebar away on a wide screen as well as a narrow one, and get the full width for the page.",
|
|
3006
3788
|
"code": "<Shell\n collapsible\n bar={<Link href=\"#/\">ACME</Link>}\n side={\n <Nav label=\"Main\">\n <Link href=\"#/reports\" active>Reports</Link>\n <Link href=\"#/settings\">Settings</Link>\n </Nav>\n }\n>\n <Text>Quarterly numbers land here.</Text>\n</Shell>"
|
|
3789
|
+
},
|
|
3790
|
+
{
|
|
3791
|
+
"title": "A drawer the page controls",
|
|
3792
|
+
"description": "drawerOpen and onDrawerOpenChange hand the phone drawer to the page, so a guided tour can open it to point at a link and close it again.",
|
|
3793
|
+
"code": "<Shell\n drawerOpen={drawerOpen}\n onDrawerOpenChange={setDrawerOpen}\n bar={<Link href=\"#/\">ACME</Link>}\n side={\n <Nav label=\"Main\">\n <Link href=\"#/reports\" active>Reports</Link>\n </Nav>\n }\n>\n <Button onClick={() => setDrawerOpen(true)}>Show me the menu</Button>\n</Shell>"
|
|
3007
3794
|
}
|
|
3008
3795
|
]
|
|
3009
3796
|
},
|
|
3797
|
+
{
|
|
3798
|
+
"name": "Spinner",
|
|
3799
|
+
"category": "feedback",
|
|
3800
|
+
"summary": "A small inline busy mark for work with no measurable progress, sized to sit inside a field or beside a line of text.",
|
|
3801
|
+
"whenToUse": "Use where a whole bar or region would be too much: beside a field that is checking a value, at the end of a line that is saving, inside a combobox while results load. The label says what is happening and is announced politely; showLabel prints it beside the mark.",
|
|
3802
|
+
"whenNotToUse": "Do not use for a region whose data is loading; that is Pending or a component's loading prop. Do not use when the work can be counted; that is Progress. Do not use for a busy button; Button's loading prop marks the button itself.",
|
|
3803
|
+
"status": "experimental",
|
|
3804
|
+
"props": {
|
|
3805
|
+
"label": {
|
|
3806
|
+
"kind": "string",
|
|
3807
|
+
"description": "What is happening, such as \"Checking availability\".",
|
|
3808
|
+
"required": true
|
|
3809
|
+
},
|
|
3810
|
+
"size": {
|
|
3811
|
+
"kind": "enum",
|
|
3812
|
+
"description": "medium matches body text; small fits inside a dense row or a field.",
|
|
3813
|
+
"values": [
|
|
3814
|
+
"small",
|
|
3815
|
+
"medium"
|
|
3816
|
+
],
|
|
3817
|
+
"default": "medium"
|
|
3818
|
+
},
|
|
3819
|
+
"showLabel": {
|
|
3820
|
+
"kind": "boolean",
|
|
3821
|
+
"description": "Print the label beside the mark instead of keeping it for assistive technology only.",
|
|
3822
|
+
"default": false
|
|
3823
|
+
}
|
|
3824
|
+
},
|
|
3825
|
+
"state": {
|
|
3826
|
+
"loading": {
|
|
3827
|
+
"description": "Always present: a spinner is only rendered while work is running.",
|
|
3828
|
+
"attribute": "data-sprint-loading"
|
|
3829
|
+
},
|
|
3830
|
+
"size": {
|
|
3831
|
+
"description": "Present as small on the compact mark.",
|
|
3832
|
+
"attribute": "data-sprint-size",
|
|
3833
|
+
"values": [
|
|
3834
|
+
"small"
|
|
3835
|
+
]
|
|
3836
|
+
}
|
|
3837
|
+
},
|
|
3838
|
+
"agentView": {
|
|
3839
|
+
"example": "- **Spinner** \"Saving note\" [loading]"
|
|
3840
|
+
},
|
|
3841
|
+
"examples": [
|
|
3842
|
+
{
|
|
3843
|
+
"title": "Saving beside a line",
|
|
3844
|
+
"description": "The mark sits in the line, and the label is printed beside it.",
|
|
3845
|
+
"code": "<Spinner label=\"Saving note\" showLabel />"
|
|
3846
|
+
},
|
|
3847
|
+
{
|
|
3848
|
+
"title": "A silent mark in a field",
|
|
3849
|
+
"description": "A small mark with the label kept for assistive technology and agents.",
|
|
3850
|
+
"code": "<Spinner label=\"Searching members\" size=\"small\" />"
|
|
3851
|
+
}
|
|
3852
|
+
],
|
|
3853
|
+
"a11y": {
|
|
3854
|
+
"role": "status",
|
|
3855
|
+
"notes": "A role=status element, so the label is announced politely when the spinner appears. Render it only while work runs, rather than toggling its visibility. The mark is a stepped four-cell pulse, still under reduced motion."
|
|
3856
|
+
}
|
|
3857
|
+
},
|
|
3010
3858
|
{
|
|
3011
3859
|
"name": "Stack",
|
|
3012
3860
|
"category": "layout",
|
|
@@ -3032,15 +3880,36 @@
|
|
|
3032
3880
|
},
|
|
3033
3881
|
"gap": {
|
|
3034
3882
|
"kind": "enum",
|
|
3035
|
-
"description": "Space between items, from the space scale.",
|
|
3883
|
+
"description": "Space between items, from the space scale: none, hairline, snug, tight, medium, normal, loose, wide, vast, smallest to largest.",
|
|
3036
3884
|
"values": [
|
|
3037
3885
|
"none",
|
|
3886
|
+
"hairline",
|
|
3887
|
+
"snug",
|
|
3038
3888
|
"tight",
|
|
3889
|
+
"medium",
|
|
3039
3890
|
"normal",
|
|
3040
|
-
"loose"
|
|
3891
|
+
"loose",
|
|
3892
|
+
"wide",
|
|
3893
|
+
"vast"
|
|
3041
3894
|
],
|
|
3042
3895
|
"default": "normal"
|
|
3043
3896
|
},
|
|
3897
|
+
"padding": {
|
|
3898
|
+
"kind": "enum",
|
|
3899
|
+
"description": "Space inside the stack's edges, on the same scale as gap. Use it for a stack that is itself a bordered or filled region; between siblings, prefer the parent's gap.",
|
|
3900
|
+
"values": [
|
|
3901
|
+
"none",
|
|
3902
|
+
"hairline",
|
|
3903
|
+
"snug",
|
|
3904
|
+
"tight",
|
|
3905
|
+
"medium",
|
|
3906
|
+
"normal",
|
|
3907
|
+
"loose",
|
|
3908
|
+
"wide",
|
|
3909
|
+
"vast"
|
|
3910
|
+
],
|
|
3911
|
+
"default": "none"
|
|
3912
|
+
},
|
|
3044
3913
|
"align": {
|
|
3045
3914
|
"kind": "enum",
|
|
3046
3915
|
"description": "Cross-axis alignment.",
|
|
@@ -3105,9 +3974,28 @@
|
|
|
3105
3974
|
"attribute": "data-sprint-gap",
|
|
3106
3975
|
"values": [
|
|
3107
3976
|
"none",
|
|
3977
|
+
"hairline",
|
|
3978
|
+
"snug",
|
|
3979
|
+
"tight",
|
|
3980
|
+
"medium",
|
|
3981
|
+
"normal",
|
|
3982
|
+
"loose",
|
|
3983
|
+
"wide",
|
|
3984
|
+
"vast"
|
|
3985
|
+
]
|
|
3986
|
+
},
|
|
3987
|
+
"padding": {
|
|
3988
|
+
"description": "The inner spacing step, when there is one.",
|
|
3989
|
+
"attribute": "data-sprint-padding",
|
|
3990
|
+
"values": [
|
|
3991
|
+
"hairline",
|
|
3992
|
+
"snug",
|
|
3108
3993
|
"tight",
|
|
3994
|
+
"medium",
|
|
3109
3995
|
"normal",
|
|
3110
|
-
"loose"
|
|
3996
|
+
"loose",
|
|
3997
|
+
"wide",
|
|
3998
|
+
"vast"
|
|
3111
3999
|
]
|
|
3112
4000
|
},
|
|
3113
4001
|
"align": {
|
|
@@ -3158,6 +4046,11 @@
|
|
|
3158
4046
|
{
|
|
3159
4047
|
"title": "A header bar that stacks on a phone",
|
|
3160
4048
|
"code": "<Stack direction=\"row\" justify=\"between\" align=\"center\" collapse>\n <Heading level={1}>Button</Heading>\n <Tag tone=\"warning\">experimental</Tag>\n</Stack>"
|
|
4049
|
+
},
|
|
4050
|
+
{
|
|
4051
|
+
"title": "A padded, dense list",
|
|
4052
|
+
"description": "padding insets the stack's own content, and a small gap packs rows closely, without a wrapper element or a class.",
|
|
4053
|
+
"code": "<Stack gap=\"snug\" padding=\"medium\">\n <Text>Opening hymn</Text>\n <Text>Invocation</Text>\n <Text>Sacrament hymn</Text>\n</Stack>"
|
|
3161
4054
|
}
|
|
3162
4055
|
]
|
|
3163
4056
|
},
|
|
@@ -3442,6 +4335,103 @@
|
|
|
3442
4335
|
"notes": "Column headers keep scope=col in every layout. On narrow screens each cell repeats its column header visually, marked aria-hidden so the real header association is not announced twice."
|
|
3443
4336
|
}
|
|
3444
4337
|
},
|
|
4338
|
+
{
|
|
4339
|
+
"name": "Tabs",
|
|
4340
|
+
"category": "navigation",
|
|
4341
|
+
"summary": "Sibling views of one subject, one shown at a time, with real tablist, tab and tabpanel semantics. Registers one select tool enumerating the tabs.",
|
|
4342
|
+
"whenToUse": "Use to switch between a few views of the same thing without leaving the page: coming up, past and by member for one set of speakers; details and history for one record. Tabs hold data: each is { value, label, panel, count?, disabled? }, and only the selected tab's panel is mounted. An optional actions slot sits at the end of the tab row.",
|
|
4343
|
+
"whenNotToUse": "Do not use to pick a value that filters or changes something else on the page; that is a SegmentedControl, a radio group. Do not use for navigation between pages with their own URLs; that is a Nav. Do not use when a person needs to compare the views side by side.",
|
|
4344
|
+
"status": "experimental",
|
|
4345
|
+
"props": {
|
|
4346
|
+
"label": {
|
|
4347
|
+
"kind": "string",
|
|
4348
|
+
"description": "What the tabs switch between, such as \"Speakers\". Names the tablist and derives the select tool name.",
|
|
4349
|
+
"required": true
|
|
4350
|
+
},
|
|
4351
|
+
"tabs": {
|
|
4352
|
+
"kind": "array",
|
|
4353
|
+
"description": "The tabs in order: { value, label, panel, count?, disabled? }. panel is the content shown while the tab is selected and may hold any components. count renders as a chip beside the label and reaches the agent view as part state.",
|
|
4354
|
+
"required": true
|
|
4355
|
+
},
|
|
4356
|
+
"value": {
|
|
4357
|
+
"kind": "string",
|
|
4358
|
+
"description": "The selected tab's value, when the page owns that state. Pair it with onChange. A disabled or unknown value falls back to the first enabled tab."
|
|
4359
|
+
},
|
|
4360
|
+
"defaultValue": {
|
|
4361
|
+
"kind": "string",
|
|
4362
|
+
"description": "The tab selected first when the Tabs keep their own state."
|
|
4363
|
+
},
|
|
4364
|
+
"onChange": {
|
|
4365
|
+
"kind": "handler",
|
|
4366
|
+
"description": "Called with the value of the tab a person or an agent selects."
|
|
4367
|
+
},
|
|
4368
|
+
"actions": {
|
|
4369
|
+
"kind": "node",
|
|
4370
|
+
"description": "Controls at the end of the tab row, such as an add Button that applies to every tab."
|
|
4371
|
+
},
|
|
4372
|
+
"agentName": {
|
|
4373
|
+
"kind": "string",
|
|
4374
|
+
"description": "Override the label used to derive the tool name."
|
|
4375
|
+
},
|
|
4376
|
+
"agentTool": {
|
|
4377
|
+
"kind": "boolean",
|
|
4378
|
+
"description": "Set false to render the tabs without registering the select tool.",
|
|
4379
|
+
"default": true
|
|
4380
|
+
}
|
|
4381
|
+
},
|
|
4382
|
+
"state": {
|
|
4383
|
+
"value": {
|
|
4384
|
+
"description": "The selected tab's label.",
|
|
4385
|
+
"attribute": "data-sprint-value"
|
|
4386
|
+
}
|
|
4387
|
+
},
|
|
4388
|
+
"tools": {
|
|
4389
|
+
"select": {
|
|
4390
|
+
"verb": "select",
|
|
4391
|
+
"description": "Show one of these tabs by its visible label, exactly as a person clicking the tab would. Only the selected tab's panel is on the page, so read the page again after switching to see its contents. Returns the tabs' state after the change.",
|
|
4392
|
+
"inputSchema": {
|
|
4393
|
+
"type": "object",
|
|
4394
|
+
"properties": {
|
|
4395
|
+
"tab": {
|
|
4396
|
+
"type": "string",
|
|
4397
|
+
"description": "The visible label of the tab to show."
|
|
4398
|
+
}
|
|
4399
|
+
},
|
|
4400
|
+
"required": [
|
|
4401
|
+
"tab"
|
|
4402
|
+
]
|
|
4403
|
+
},
|
|
4404
|
+
"readOnly": false,
|
|
4405
|
+
"untrustedContent": true,
|
|
4406
|
+
"registeredWhen": "The tabs are mounted, at least one tab is enabled, and no other component claims the same tool name. The registered schema enumerates the enabled tabs' labels.",
|
|
4407
|
+
"unregisteredWhen": "The tabs unmount or every tab is disabled."
|
|
4408
|
+
}
|
|
4409
|
+
},
|
|
4410
|
+
"agentView": {
|
|
4411
|
+
"example": "- **Tabs** \"Speakers\" [value=Coming up] → tool `select-speakers`\n - part `tab` \"Coming up\" [count=4, selected]\n - part `tab` \"Past\""
|
|
4412
|
+
},
|
|
4413
|
+
"examples": [
|
|
4414
|
+
{
|
|
4415
|
+
"title": "Views of one subject",
|
|
4416
|
+
"description": "Only the selected panel is on the page. An agent switches with the select tool or the tab controls in the agent view, then reads the new panel.",
|
|
4417
|
+
"code": "<Tabs\n label=\"Speakers\"\n tabs={[\n { value: \"upcoming\", label: \"Coming up\", count: 4, panel: <UpcomingSpeakers /> },\n { value: \"past\", label: \"Past\", panel: <PastSpeakers /> },\n { value: \"member\", label: \"By member\", panel: <SpeakersByMember /> },\n ]}\n/>"
|
|
4418
|
+
},
|
|
4419
|
+
{
|
|
4420
|
+
"title": "Tabs with an action",
|
|
4421
|
+
"description": "actions sits at the end of the tab row and stays put while the panels change. A disabled tab is visible but cannot be selected.",
|
|
4422
|
+
"code": "<Tabs\n label=\"Record\"\n value={view}\n onChange={setView}\n actions={<Button size=\"small\">Export</Button>}\n tabs={[\n { value: \"details\", label: \"Details\", panel: <Details /> },\n { value: \"history\", label: \"History\", panel: <History /> },\n { value: \"audit\", label: \"Audit\", disabled: true, panel: null },\n ]}\n/>"
|
|
4423
|
+
}
|
|
4424
|
+
],
|
|
4425
|
+
"a11y": {
|
|
4426
|
+
"role": "tablist",
|
|
4427
|
+
"keyboard": [
|
|
4428
|
+
"Left and Right Arrow move to the previous or next enabled tab and show it",
|
|
4429
|
+
"Home and End show the first and last enabled tab",
|
|
4430
|
+
"Tab moves from the selected tab into its panel"
|
|
4431
|
+
],
|
|
4432
|
+
"notes": "Roving tabindex across the tabs, with selection following focus. The panel is a focusable tabpanel labelled by its tab. A count chip is hidden from assistive technology, so the tab is named by its label alone."
|
|
4433
|
+
}
|
|
4434
|
+
},
|
|
3445
4435
|
{
|
|
3446
4436
|
"name": "Tag",
|
|
3447
4437
|
"category": "display",
|
|
@@ -3472,6 +4462,11 @@
|
|
|
3472
4462
|
"kind": "boolean",
|
|
3473
4463
|
"description": "Render as a solid field of the tone with inverted ink, instead of a keyline. Use for the one chip that must be read first.",
|
|
3474
4464
|
"default": false
|
|
4465
|
+
},
|
|
4466
|
+
"provisional": {
|
|
4467
|
+
"kind": "boolean",
|
|
4468
|
+
"description": "Draw a dashed keyline for something that is not real yet: a draft, a scenario, a proposed change. It reads as provisional to agents too.",
|
|
4469
|
+
"default": false
|
|
3475
4470
|
}
|
|
3476
4471
|
},
|
|
3477
4472
|
"state": {
|
|
@@ -3490,12 +4485,21 @@
|
|
|
3490
4485
|
"filled": {
|
|
3491
4486
|
"description": "Present when the chip is a solid field rather than a keyline.",
|
|
3492
4487
|
"attribute": "data-sprint-filled"
|
|
4488
|
+
},
|
|
4489
|
+
"provisional": {
|
|
4490
|
+
"description": "Present on a chip for something that is not real yet.",
|
|
4491
|
+
"attribute": "data-sprint-provisional"
|
|
3493
4492
|
}
|
|
3494
4493
|
},
|
|
3495
4494
|
"agentView": {
|
|
3496
4495
|
"example": "- **Tag** \"experimental\" [filled, tone=warning]"
|
|
3497
4496
|
},
|
|
3498
4497
|
"examples": [
|
|
4498
|
+
{
|
|
4499
|
+
"title": "A provisional chip",
|
|
4500
|
+
"description": "A dashed keyline separates a scenario from the live roster without spending a colour.",
|
|
4501
|
+
"code": "<Tag tone=\"info\" provisional>Scenario: fall reshuffle</Tag>"
|
|
4502
|
+
},
|
|
3499
4503
|
{
|
|
3500
4504
|
"title": "A release status",
|
|
3501
4505
|
"code": "<Tag tone=\"warning\" filled>experimental</Tag>"
|
|
@@ -3546,6 +4550,34 @@
|
|
|
3546
4550
|
],
|
|
3547
4551
|
"default": "normal"
|
|
3548
4552
|
},
|
|
4553
|
+
"weight": {
|
|
4554
|
+
"kind": "enum",
|
|
4555
|
+
"description": "bold for a line that needs to stand out among its neighbours, such as a name in a list row. Prefer a Heading for a title.",
|
|
4556
|
+
"values": [
|
|
4557
|
+
"normal",
|
|
4558
|
+
"bold"
|
|
4559
|
+
],
|
|
4560
|
+
"default": "normal"
|
|
4561
|
+
},
|
|
4562
|
+
"align": {
|
|
4563
|
+
"kind": "enum",
|
|
4564
|
+
"description": "Horizontal alignment, in the writing direction.",
|
|
4565
|
+
"values": [
|
|
4566
|
+
"start",
|
|
4567
|
+
"center",
|
|
4568
|
+
"end"
|
|
4569
|
+
],
|
|
4570
|
+
"default": "start"
|
|
4571
|
+
},
|
|
4572
|
+
"lines": {
|
|
4573
|
+
"kind": "number",
|
|
4574
|
+
"description": "Clamp the text to this many lines, 1 to 6, ending in an ellipsis. Only the screen is clamped: assistive technology and the agent view still get the whole text. Pair it with a Tooltip when a sighted person needs the rest."
|
|
4575
|
+
},
|
|
4576
|
+
"italic": {
|
|
4577
|
+
"kind": "boolean",
|
|
4578
|
+
"description": "Set in italic, for a quotation, a title of a work, or a scripture reference.",
|
|
4579
|
+
"default": false
|
|
4580
|
+
},
|
|
3549
4581
|
"as": {
|
|
3550
4582
|
"kind": "enum",
|
|
3551
4583
|
"description": "The element to render. Use span when the text sits inside another line of text.",
|
|
@@ -3577,6 +4609,29 @@
|
|
|
3577
4609
|
"small",
|
|
3578
4610
|
"normal"
|
|
3579
4611
|
]
|
|
4612
|
+
},
|
|
4613
|
+
"weight": {
|
|
4614
|
+
"description": "Present as bold on emphasised text.",
|
|
4615
|
+
"attribute": "data-sprint-weight",
|
|
4616
|
+
"values": [
|
|
4617
|
+
"bold"
|
|
4618
|
+
]
|
|
4619
|
+
},
|
|
4620
|
+
"align": {
|
|
4621
|
+
"description": "The alignment, when it is not start.",
|
|
4622
|
+
"attribute": "data-sprint-align",
|
|
4623
|
+
"values": [
|
|
4624
|
+
"center",
|
|
4625
|
+
"end"
|
|
4626
|
+
]
|
|
4627
|
+
},
|
|
4628
|
+
"lines": {
|
|
4629
|
+
"description": "The line clamp, when one is set. The text itself is never shortened.",
|
|
4630
|
+
"attribute": "data-sprint-lines"
|
|
4631
|
+
},
|
|
4632
|
+
"italic": {
|
|
4633
|
+
"description": "Present on italic text.",
|
|
4634
|
+
"attribute": "data-sprint-italic"
|
|
3580
4635
|
}
|
|
3581
4636
|
},
|
|
3582
4637
|
"agentView": {
|
|
@@ -3596,6 +4651,15 @@
|
|
|
3596
4651
|
"title": "A live status line",
|
|
3597
4652
|
"description": "Tone is the whole message here, so an agent reading the attribute learns the same thing a person learns from the colour.",
|
|
3598
4653
|
"code": "<Text tone={ready ? \"action\" : \"warning\"} size=\"small\">\n {ready ? \"WebMCP is available in this browser.\" : \"WebMCP is unavailable here.\"}\n</Text>"
|
|
4654
|
+
},
|
|
4655
|
+
{
|
|
4656
|
+
"title": "A clamped line",
|
|
4657
|
+
"description": "lines clamps the screen to two lines with an ellipsis. The agent view and screen readers still get every word.",
|
|
4658
|
+
"code": "<Text lines={2}>{member.notes}</Text>"
|
|
4659
|
+
},
|
|
4660
|
+
{
|
|
4661
|
+
"title": "Emphasis and alignment",
|
|
4662
|
+
"code": "<Text weight=\"bold\" align=\"center\" italic>Come, follow me.</Text>"
|
|
3599
4663
|
}
|
|
3600
4664
|
]
|
|
3601
4665
|
},
|
|
@@ -3624,9 +4688,14 @@
|
|
|
3624
4688
|
},
|
|
3625
4689
|
"rows": {
|
|
3626
4690
|
"kind": "number",
|
|
3627
|
-
"description": "The visible line count before scrolling.",
|
|
4691
|
+
"description": "The visible line count before scrolling. With autoGrow it is the minimum height.",
|
|
3628
4692
|
"default": 4
|
|
3629
4693
|
},
|
|
4694
|
+
"autoGrow": {
|
|
4695
|
+
"kind": "boolean",
|
|
4696
|
+
"description": "Grow the area with its content instead of scrolling, never shorter than rows.",
|
|
4697
|
+
"default": false
|
|
4698
|
+
},
|
|
3630
4699
|
"placeholder": {
|
|
3631
4700
|
"kind": "string",
|
|
3632
4701
|
"description": "Ghost text shown while the area is empty."
|
|
@@ -3653,6 +4722,19 @@
|
|
|
3653
4722
|
"description": "Mark the area required, visually and in the agent view.",
|
|
3654
4723
|
"default": false
|
|
3655
4724
|
},
|
|
4725
|
+
"hideLabel": {
|
|
4726
|
+
"kind": "boolean",
|
|
4727
|
+
"description": "Hide the label visually while keeping it as the field's accessible name and agent label. Pair it with a placeholder or a nearby heading so sighted people still know what the field is for.",
|
|
4728
|
+
"default": false
|
|
4729
|
+
},
|
|
4730
|
+
"inputRef": {
|
|
4731
|
+
"kind": "object",
|
|
4732
|
+
"description": "A ref to the underlying <textarea>, for focusing or measuring it. The component's own ref points at the wrapper."
|
|
4733
|
+
},
|
|
4734
|
+
"inputProps": {
|
|
4735
|
+
"kind": "object",
|
|
4736
|
+
"description": "Extra native attributes and handlers for the <textarea> itself, such as autoFocus, maxLength, inputMode, onKeyDown, or onBlur. Props spread on the component land on the wrapper; these land on the area. Anything the component manages (value, onChange, disabled, the error wiring) cannot be overridden here."
|
|
4737
|
+
},
|
|
3656
4738
|
"agentName": {
|
|
3657
4739
|
"kind": "string",
|
|
3658
4740
|
"description": "Override the label used to derive the tool name, when two areas on a page would otherwise collide."
|
|
@@ -3720,6 +4802,11 @@
|
|
|
3720
4802
|
"title": "A required area with an error",
|
|
3721
4803
|
"description": "The error replaces the hint and marks the area invalid on every surface.",
|
|
3722
4804
|
"code": "<Textarea\n label=\"Abort reason\"\n value={reason}\n onChange={setReason}\n required\n rows={3}\n error=\"State the reason before aborting.\"\n/>"
|
|
4805
|
+
},
|
|
4806
|
+
{
|
|
4807
|
+
"title": "An area that grows",
|
|
4808
|
+
"description": "autoGrow drops the scrollbar: the area starts at rows lines and grows with what is typed. The label is hidden because a heading above already names it, and inputProps puts a length cap and a key handler on the textarea itself.",
|
|
4809
|
+
"code": "<Textarea\n label=\"Log entry\"\n hideLabel\n autoGrow\n rows={2}\n value={entry}\n onChange={setEntry}\n placeholder=\"What happened on this pass\"\n inputProps={{ maxLength: 500, onKeyDown: submitOnModEnter }}\n/>"
|
|
3723
4810
|
}
|
|
3724
4811
|
],
|
|
3725
4812
|
"a11y": {
|
|
@@ -3756,13 +4843,15 @@
|
|
|
3756
4843
|
},
|
|
3757
4844
|
"type": {
|
|
3758
4845
|
"kind": "enum",
|
|
3759
|
-
"description": "The input type. \"password\" masks the field everywhere: the value never appears in agent attributes, the agent view, or tool results.",
|
|
4846
|
+
"description": "The input type. \"password\" masks the field everywhere: the value never appears in agent attributes, the agent view, or tool results. \"number\" keeps value a string, so the page parses it; pass min, max and step through inputProps.",
|
|
3760
4847
|
"values": [
|
|
3761
4848
|
"text",
|
|
3762
4849
|
"email",
|
|
3763
4850
|
"url",
|
|
3764
4851
|
"search",
|
|
3765
|
-
"password"
|
|
4852
|
+
"password",
|
|
4853
|
+
"number",
|
|
4854
|
+
"tel"
|
|
3766
4855
|
],
|
|
3767
4856
|
"default": "text"
|
|
3768
4857
|
},
|
|
@@ -3801,6 +4890,27 @@
|
|
|
3801
4890
|
"description": "Mark the field required, visually and in the agent view.",
|
|
3802
4891
|
"default": false
|
|
3803
4892
|
},
|
|
4893
|
+
"hideLabel": {
|
|
4894
|
+
"kind": "boolean",
|
|
4895
|
+
"description": "Hide the label visually while keeping it as the field's accessible name and agent label. Pair it with a placeholder or a nearby heading so sighted people still know what the field is for.",
|
|
4896
|
+
"default": false
|
|
4897
|
+
},
|
|
4898
|
+
"icon": {
|
|
4899
|
+
"kind": "node",
|
|
4900
|
+
"description": "A decorative icon drawn inside the start of the field, such as a magnifier on a search box. Hidden from assistive technology."
|
|
4901
|
+
},
|
|
4902
|
+
"trailing": {
|
|
4903
|
+
"kind": "node",
|
|
4904
|
+
"description": "Controls drawn inside the end of the field, such as a clear Button with hideLabel and size=\"small\". It renders only in the human view; give an agent the same action some other way, or rely on the fill tool, which can set the field to empty."
|
|
4905
|
+
},
|
|
4906
|
+
"inputRef": {
|
|
4907
|
+
"kind": "object",
|
|
4908
|
+
"description": "A ref to the underlying <input>, for focusing or measuring it. The component's own ref points at the wrapper."
|
|
4909
|
+
},
|
|
4910
|
+
"inputProps": {
|
|
4911
|
+
"kind": "object",
|
|
4912
|
+
"description": "Extra native attributes and handlers for the <input> itself, such as autoFocus, maxLength, inputMode, onKeyDown, or onBlur. Props spread on the component land on the wrapper; these land on the field. Anything the component manages (value, onChange, disabled, the error wiring) cannot be overridden here."
|
|
4913
|
+
},
|
|
3804
4914
|
"agentName": {
|
|
3805
4915
|
"kind": "string",
|
|
3806
4916
|
"description": "Override the label used to derive the tool name, when two fields on a page would otherwise collide."
|
|
@@ -3886,6 +4996,16 @@
|
|
|
3886
4996
|
"title": "A read-only value",
|
|
3887
4997
|
"description": "A value shown in the shape of a form field that nobody may edit. It stays focusable and selectable, reads as text in the agent view, and registers no fill tool. To hand someone a value to paste elsewhere, a CopyField adds the copy control.",
|
|
3888
4998
|
"code": "<TextInput\n label=\"Station ID\"\n value=\"KX-2209-ALPHA\"\n onChange={() => {}}\n readOnly\n hint=\"Assigned at registration\"\n/>"
|
|
4999
|
+
},
|
|
5000
|
+
{
|
|
5001
|
+
"title": "A number with field attributes",
|
|
5002
|
+
"description": "type=\"number\" keeps value a string. Native attributes for the input itself, such as min, max and autoFocus, go through inputProps, and inputRef reaches the input for focusing it later.",
|
|
5003
|
+
"code": "<TextInput\n label=\"Link expiry in days\"\n type=\"number\"\n value={days}\n onChange={setDays}\n inputRef={daysField}\n inputProps={{ min: 1, max: 90, step: 1 }}\n/>"
|
|
5004
|
+
},
|
|
5005
|
+
{
|
|
5006
|
+
"title": "A search field with a clear control",
|
|
5007
|
+
"description": "The icon sits inside the start of the field and a small icon-only Button clears it from the end. An agent clears it by filling an empty string.",
|
|
5008
|
+
"code": "<TextInput\n label=\"Filter the board\"\n hideLabel\n placeholder=\"Filter by name or calling\"\n icon={<SearchIcon />}\n value={query}\n onChange={setQuery}\n trailing={\n query === \"\" ? null : (\n <Button size=\"small\" icon={<CloseIcon />} hideLabel onClick={() => setQuery(\"\")}>\n Clear filter\n </Button>\n )\n }\n/>"
|
|
3889
5009
|
}
|
|
3890
5010
|
],
|
|
3891
5011
|
"a11y": {
|
|
@@ -3896,6 +5016,194 @@
|
|
|
3896
5016
|
],
|
|
3897
5017
|
"notes": "The label element is associated via htmlFor. An error sets aria-invalid and is linked with aria-describedby, as is the hint. Focus is an offset keyline, never a rounded ring."
|
|
3898
5018
|
}
|
|
5019
|
+
},
|
|
5020
|
+
{
|
|
5021
|
+
"name": "Toast",
|
|
5022
|
+
"category": "feedback",
|
|
5023
|
+
"summary": "A brief message that floats at the bottom of the screen after something happened, with an optional single action such as Undo, and dismisses itself.",
|
|
5024
|
+
"whenToUse": "Use to confirm an action that already took effect and can still be reversed or followed up: a person moved, a note saved, a share link copied. Render one Toast with open, message and onDismiss; the page owns which toast is showing. The action is data, { label, onSelect, shortcut? }, rendered as a Button that registers its own press tool, and the shortcut is shown as key caps.",
|
|
5025
|
+
"whenNotToUse": "Do not use for an error the person must act on, or anything that must stay visible; that is an Alert in the page. Do not use for a decision; that is a Dialog. Do not show several at once: replace the message instead. Inside an open modal Dialog, render the Toast inside the Dialog, because the page behind it is inert.",
|
|
5026
|
+
"status": "experimental",
|
|
5027
|
+
"props": {
|
|
5028
|
+
"open": {
|
|
5029
|
+
"kind": "boolean",
|
|
5030
|
+
"description": "Whether the toast is showing. A closed toast renders nothing.",
|
|
5031
|
+
"required": true
|
|
5032
|
+
},
|
|
5033
|
+
"message": {
|
|
5034
|
+
"kind": "string",
|
|
5035
|
+
"description": "What happened, in a sentence, such as \"Moved Sister Amaral to Primary.\"",
|
|
5036
|
+
"required": true
|
|
5037
|
+
},
|
|
5038
|
+
"onDismiss": {
|
|
5039
|
+
"kind": "handler",
|
|
5040
|
+
"description": "Called when the toast should go: after duration, or when the dismiss control is pressed. Set open to false in response.",
|
|
5041
|
+
"required": true
|
|
5042
|
+
},
|
|
5043
|
+
"label": {
|
|
5044
|
+
"kind": "string",
|
|
5045
|
+
"description": "A short title above the message, and the toast's accessible name."
|
|
5046
|
+
},
|
|
5047
|
+
"tone": {
|
|
5048
|
+
"kind": "enum",
|
|
5049
|
+
"description": "neutral and info announce politely; warning too. danger announces assertively, but prefer an Alert for errors.",
|
|
5050
|
+
"values": [
|
|
5051
|
+
"neutral",
|
|
5052
|
+
"info",
|
|
5053
|
+
"warning",
|
|
5054
|
+
"danger"
|
|
5055
|
+
],
|
|
5056
|
+
"default": "neutral"
|
|
5057
|
+
},
|
|
5058
|
+
"action": {
|
|
5059
|
+
"kind": "object",
|
|
5060
|
+
"description": "One follow-up action: { label, onSelect, shortcut? }. It is a Button with its own press tool, named from the label. shortcut is shown as key caps and published as aria-keyshortcuts; the page binds the key itself."
|
|
5061
|
+
},
|
|
5062
|
+
"duration": {
|
|
5063
|
+
"kind": "number",
|
|
5064
|
+
"description": "Milliseconds before onDismiss is called. The timer pauses while the pointer or focus is on the toast. Pass null to keep it until dismissed.",
|
|
5065
|
+
"default": 6000
|
|
5066
|
+
},
|
|
5067
|
+
"dismissLabel": {
|
|
5068
|
+
"kind": "string",
|
|
5069
|
+
"description": "Accessible name of the dismiss control.",
|
|
5070
|
+
"default": "Dismiss"
|
|
5071
|
+
}
|
|
5072
|
+
},
|
|
5073
|
+
"state": {
|
|
5074
|
+
"tone": {
|
|
5075
|
+
"description": "The toast's tone.",
|
|
5076
|
+
"attribute": "data-sprint-tone",
|
|
5077
|
+
"values": [
|
|
5078
|
+
"neutral",
|
|
5079
|
+
"info",
|
|
5080
|
+
"warning",
|
|
5081
|
+
"danger"
|
|
5082
|
+
]
|
|
5083
|
+
}
|
|
5084
|
+
},
|
|
5085
|
+
"agentView": {
|
|
5086
|
+
"example": "- **Toast** [tone=neutral]\n - part `message` \"Moved Sister Amaral to Primary.\"\n - part `dismiss` \"Dismiss\"\n - **Button** \"Undo\" [size=small, tone=action]"
|
|
5087
|
+
},
|
|
5088
|
+
"examples": [
|
|
5089
|
+
{
|
|
5090
|
+
"title": "An undo toast",
|
|
5091
|
+
"description": "The action is a real Button, so an agent can press Undo through its tool while the toast is up.",
|
|
5092
|
+
"code": "<Toast\n open={moved !== null}\n message={movedMessage}\n action={{ label: \"Undo\", onSelect: undo, shortcut: \"Ctrl+Z\" }}\n onDismiss={() => setMoved(null)}\n/>"
|
|
5093
|
+
},
|
|
5094
|
+
{
|
|
5095
|
+
"title": "A toast that stays",
|
|
5096
|
+
"description": "duration={null} keeps it until dismissed.",
|
|
5097
|
+
"code": "<Toast\n open={offline}\n label=\"Offline\"\n tone=\"warning\"\n duration={null}\n message=\"Changes are saved on this device until the connection returns.\"\n onDismiss={() => setOffline(false)}\n/>"
|
|
5098
|
+
}
|
|
5099
|
+
],
|
|
5100
|
+
"a11y": {
|
|
5101
|
+
"role": "status",
|
|
5102
|
+
"notes": "A role=status region, or role=alert for danger, so the message is announced when the toast appears. Render it when the event happens rather than toggling visibility. It opens as a popover in the top layer, at the bottom edge on a phone and the bottom corner on a wide screen. The timer pauses while the pointer or keyboard focus is on it, so nobody loses the action mid-reach."
|
|
5103
|
+
}
|
|
5104
|
+
},
|
|
5105
|
+
{
|
|
5106
|
+
"name": "Tooltip",
|
|
5107
|
+
"category": "overlay",
|
|
5108
|
+
"summary": "A short hint that appears beside one control on hover or keyboard focus. A human affordance only: it adds nothing to the agent view.",
|
|
5109
|
+
"whenToUse": "Use to name an icon-only control for sighted mouse users, or to show the whole of a line that is truncated on screen. Wrap exactly one focusable element. The hint appears after a short hover or at once on keyboard focus, and Escape dismisses it.",
|
|
5110
|
+
"whenNotToUse": "Never put information only in a tooltip: touch users never see it and agents never read it, so the text must repeat something the wrapped element already says through its label or its own content. Do not use it for anything interactive; that is a Menu or a Dialog. For an icon-only Button, pass hideLabel instead, which adds the tooltip itself.",
|
|
5111
|
+
"status": "experimental",
|
|
5112
|
+
"props": {
|
|
5113
|
+
"label": {
|
|
5114
|
+
"kind": "string",
|
|
5115
|
+
"description": "The hint text. Keep it to a few words.",
|
|
5116
|
+
"required": true
|
|
5117
|
+
},
|
|
5118
|
+
"children": {
|
|
5119
|
+
"kind": "node",
|
|
5120
|
+
"description": "Exactly one focusable element, such as a Button or a link. The tooltip is anchored to it and, unless describe is false, linked to it with aria-describedby.",
|
|
5121
|
+
"required": true
|
|
5122
|
+
},
|
|
5123
|
+
"side": {
|
|
5124
|
+
"kind": "enum",
|
|
5125
|
+
"description": "Where the hint prefers to appear. It flips to the other side when there is no room.",
|
|
5126
|
+
"values": [
|
|
5127
|
+
"above",
|
|
5128
|
+
"below"
|
|
5129
|
+
],
|
|
5130
|
+
"default": "above"
|
|
5131
|
+
},
|
|
5132
|
+
"describe": {
|
|
5133
|
+
"kind": "boolean",
|
|
5134
|
+
"description": "Link the hint to the element with aria-describedby. Set false when the hint repeats the element's accessible name, so a screen reader does not read it twice.",
|
|
5135
|
+
"default": true
|
|
5136
|
+
},
|
|
5137
|
+
"disabled": {
|
|
5138
|
+
"kind": "boolean",
|
|
5139
|
+
"description": "Stop the hint from appearing.",
|
|
5140
|
+
"default": false
|
|
5141
|
+
}
|
|
5142
|
+
},
|
|
5143
|
+
"examples": [
|
|
5144
|
+
{
|
|
5145
|
+
"title": "Naming a truncated line",
|
|
5146
|
+
"description": "The line is cut short on screen, so the tooltip shows it whole. The element's own text already carries the full value for assistive technology and agents.",
|
|
5147
|
+
"code": "<Tooltip label=\"Elder Kestrel, second counselor in the elders quorum presidency\">\n <Link href=\"/callings/42\">Elder Kestrel, second counselor…</Link>\n</Tooltip>"
|
|
5148
|
+
},
|
|
5149
|
+
{
|
|
5150
|
+
"title": "A hint below its control",
|
|
5151
|
+
"description": "side moves the hint below the control; it still flips when the control sits at the bottom of the screen.",
|
|
5152
|
+
"code": "<Tooltip label=\"Opens in the planner\" side=\"below\">\n <Button>Plan Sunday</Button>\n</Tooltip>"
|
|
5153
|
+
}
|
|
5154
|
+
],
|
|
5155
|
+
"a11y": {
|
|
5156
|
+
"role": "tooltip",
|
|
5157
|
+
"keyboard": [
|
|
5158
|
+
"Focus shows the hint",
|
|
5159
|
+
"Escape hides it"
|
|
5160
|
+
],
|
|
5161
|
+
"notes": "The hint is a role=tooltip element linked to the wrapped element with aria-describedby. It appears after a hover delay or immediately on keyboard focus, never on touch, and is dismissed by Escape, blur, or moving the pointer away. It is a popover in the top layer, rendered inside the wrapped element's DOM, so it shows above a modal Dialog."
|
|
5162
|
+
}
|
|
5163
|
+
},
|
|
5164
|
+
{
|
|
5165
|
+
"name": "VisuallyHidden",
|
|
5166
|
+
"category": "typography",
|
|
5167
|
+
"summary": "Text that is off screen but still read by screen readers and agents. For context a sighted person gets from the layout.",
|
|
5168
|
+
"whenToUse": "Use to say in words what the layout says visually: \"(opens in a new tab)\" after a link, a count's unit, a heading for a region whose purpose is obvious on screen. With focusable, it wraps a skip link that appears only while it has focus.",
|
|
5169
|
+
"whenNotToUse": "Do not use to hide a field's label; TextInput, Textarea, SearchField and Progress take hideLabel, and an icon-only Button takes hideLabel too. Do not hide content that a sighted person also needs.",
|
|
5170
|
+
"status": "experimental",
|
|
5171
|
+
"props": {
|
|
5172
|
+
"children": {
|
|
5173
|
+
"kind": "node",
|
|
5174
|
+
"description": "The hidden text, or a skip link when focusable is set.",
|
|
5175
|
+
"required": true
|
|
5176
|
+
},
|
|
5177
|
+
"focusable": {
|
|
5178
|
+
"kind": "boolean",
|
|
5179
|
+
"description": "Show the content while something inside it has keyboard focus, for a skip link.",
|
|
5180
|
+
"default": false
|
|
5181
|
+
}
|
|
5182
|
+
},
|
|
5183
|
+
"state": {
|
|
5184
|
+
"focusable": {
|
|
5185
|
+
"description": "Present when the content appears while focused.",
|
|
5186
|
+
"attribute": "data-sprint-focusable"
|
|
5187
|
+
}
|
|
5188
|
+
},
|
|
5189
|
+
"agentView": {
|
|
5190
|
+
"example": "- **VisuallyHidden** \"opens in a new tab\""
|
|
5191
|
+
},
|
|
5192
|
+
"examples": [
|
|
5193
|
+
{
|
|
5194
|
+
"title": "Extra words for a screen reader",
|
|
5195
|
+
"description": "The arrow says it to a sighted person; the hidden text says it to everyone else.",
|
|
5196
|
+
"code": "<Link href=\"https://churchofjesuschrist.org\" external>\n Gospel Library ↗<VisuallyHidden> (opens in a new tab)</VisuallyHidden>\n</Link>"
|
|
5197
|
+
},
|
|
5198
|
+
{
|
|
5199
|
+
"title": "A skip link",
|
|
5200
|
+
"description": "Off screen until a keyboard user tabs to it.",
|
|
5201
|
+
"code": "<VisuallyHidden focusable>\n <Link href=\"#main\">Skip to the board</Link>\n</VisuallyHidden>"
|
|
5202
|
+
}
|
|
5203
|
+
],
|
|
5204
|
+
"a11y": {
|
|
5205
|
+
"notes": "Hidden with a one-pixel clip rather than display or visibility, so screen readers still read it. The agent view renders its text as an ordinary line."
|
|
5206
|
+
}
|
|
3899
5207
|
}
|
|
3900
5208
|
]
|
|
3901
5209
|
}
|