ionbase-ui 0.51.0 → 0.69.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/dist/components/Accordion.d.ts +54 -0
- package/dist/components/Accordion.d.ts.map +1 -0
- package/dist/components/Accordion.js +68 -0
- package/dist/components/Accordion.js.map +1 -0
- package/dist/components/AgentActivity.d.ts +2 -1
- package/dist/components/AgentActivity.d.ts.map +1 -1
- package/dist/components/AgentActivity.js +4 -17
- package/dist/components/AgentActivity.js.map +1 -1
- package/dist/components/Breadcrumb.d.ts +45 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb.js +31 -0
- package/dist/components/Breadcrumb.js.map +1 -0
- package/dist/components/Calendar.d.ts +20 -0
- package/dist/components/Calendar.d.ts.map +1 -0
- package/dist/components/Calendar.js +151 -0
- package/dist/components/Calendar.js.map +1 -0
- package/dist/components/CalendarPopover.d.ts +26 -0
- package/dist/components/CalendarPopover.d.ts.map +1 -0
- package/dist/components/CalendarPopover.js +45 -0
- package/dist/components/CalendarPopover.js.map +1 -0
- package/dist/components/Checkbox.d.ts +2 -1
- package/dist/components/Checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox.js +4 -2
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/Combobox.d.ts +86 -0
- package/dist/components/Combobox.d.ts.map +1 -0
- package/dist/components/Combobox.js +261 -0
- package/dist/components/Combobox.js.map +1 -0
- package/dist/components/DateField.d.ts +15 -0
- package/dist/components/DateField.d.ts.map +1 -0
- package/dist/components/DateField.js +74 -0
- package/dist/components/DateField.js.map +1 -0
- package/dist/components/DatePicker.d.ts +81 -0
- package/dist/components/DatePicker.d.ts.map +1 -0
- package/dist/components/DatePicker.js +102 -0
- package/dist/components/DatePicker.js.map +1 -0
- package/dist/components/DateRangePicker.d.ts +117 -0
- package/dist/components/DateRangePicker.d.ts.map +1 -0
- package/dist/components/DateRangePicker.js +152 -0
- package/dist/components/DateRangePicker.js.map +1 -0
- package/dist/components/Drawer.d.ts +52 -0
- package/dist/components/Drawer.d.ts.map +1 -0
- package/dist/components/Drawer.js +66 -0
- package/dist/components/Drawer.js.map +1 -0
- package/dist/components/FileUpload.d.ts +79 -0
- package/dist/components/FileUpload.d.ts.map +1 -0
- package/dist/components/FileUpload.js +218 -0
- package/dist/components/FileUpload.js.map +1 -0
- package/dist/components/Header.d.ts +10 -0
- package/dist/components/Header.d.ts.map +1 -1
- package/dist/components/Header.js +18 -3
- package/dist/components/Header.js.map +1 -1
- package/dist/components/NumberInput.d.ts +63 -0
- package/dist/components/NumberInput.d.ts.map +1 -0
- package/dist/components/NumberInput.js +102 -0
- package/dist/components/NumberInput.js.map +1 -0
- package/dist/components/ProgressBar.d.ts +46 -0
- package/dist/components/ProgressBar.d.ts.map +1 -0
- package/dist/components/ProgressBar.js +52 -0
- package/dist/components/ProgressBar.js.map +1 -0
- package/dist/components/PromptInput.d.ts +89 -0
- package/dist/components/PromptInput.d.ts.map +1 -0
- package/dist/components/PromptInput.js +130 -0
- package/dist/components/PromptInput.js.map +1 -0
- package/dist/components/SegmentedControl.d.ts +63 -0
- package/dist/components/SegmentedControl.d.ts.map +1 -0
- package/dist/components/SegmentedControl.js +79 -0
- package/dist/components/SegmentedControl.js.map +1 -0
- package/dist/components/Select.d.ts +9 -2
- package/dist/components/Select.d.ts.map +1 -1
- package/dist/components/Select.js.map +1 -1
- package/dist/components/Sidebar.d.ts +109 -0
- package/dist/components/Sidebar.d.ts.map +1 -0
- package/dist/components/Sidebar.js +106 -0
- package/dist/components/Sidebar.js.map +1 -0
- package/dist/components/Skeleton.d.ts +39 -0
- package/dist/components/Skeleton.d.ts.map +1 -0
- package/dist/components/Skeleton.js +50 -0
- package/dist/components/Skeleton.js.map +1 -0
- package/dist/components/Spinner.d.ts +45 -0
- package/dist/components/Spinner.d.ts.map +1 -0
- package/dist/components/Spinner.js +44 -0
- package/dist/components/Spinner.js.map +1 -0
- package/dist/components/Stepper.d.ts +73 -0
- package/dist/components/Stepper.d.ts.map +1 -0
- package/dist/components/Stepper.js +82 -0
- package/dist/components/Stepper.js.map +1 -0
- package/dist/components/Toggle.d.ts +2 -1
- package/dist/components/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle.js +4 -2
- package/dist/components/Toggle.js.map +1 -1
- package/dist/components/ToolCall.d.ts +66 -0
- package/dist/components/ToolCall.d.ts.map +1 -0
- package/dist/components/ToolCall.js +78 -0
- package/dist/components/ToolCall.js.map +1 -0
- package/dist/components/agent-status.d.ts +14 -0
- package/dist/components/agent-status.d.ts.map +1 -0
- package/dist/components/agent-status.js +16 -0
- package/dist/components/agent-status.js.map +1 -0
- package/dist/components/index.d.ts +33 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +16 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/iso-date.d.ts +53 -0
- package/dist/components/iso-date.d.ts.map +1 -0
- package/dist/components/iso-date.js +102 -0
- package/dist/components/iso-date.js.map +1 -0
- package/dist/components/resolve-selection.d.ts +36 -0
- package/dist/components/resolve-selection.d.ts.map +1 -0
- package/dist/components/resolve-selection.js +14 -0
- package/dist/components/resolve-selection.js.map +1 -0
- package/dist/figma-descriptions.json +255 -145
- package/dist/figma-map.json +3205 -1860
- package/dist/meta/Accordion.json +123 -0
- package/dist/meta/AccordionItem.json +90 -0
- package/dist/meta/AgentActivityStep.json +2 -2
- package/dist/meta/Breadcrumb.json +87 -0
- package/dist/meta/BreadcrumbItem.json +78 -0
- package/dist/meta/Checkbox.json +18 -1
- package/dist/meta/Combobox.json +265 -0
- package/dist/meta/DatePicker.json +270 -0
- package/dist/meta/DateRangePicker.json +284 -0
- package/dist/meta/Drawer.json +234 -0
- package/dist/meta/EmptyState.json +1 -1
- package/dist/meta/FileUpload.json +246 -0
- package/dist/meta/Header.json +32 -2
- package/dist/meta/NumberInput.json +407 -0
- package/dist/meta/PhoneInput.json +39 -39
- package/dist/meta/Popover.json +3 -3
- package/dist/meta/ProgressBar.json +166 -0
- package/dist/meta/PromptInput.json +212 -0
- package/dist/meta/SegmentedControl.json +217 -0
- package/dist/meta/SegmentedControlItem.json +122 -0
- package/dist/meta/Select.json +4 -3
- package/dist/meta/Sidebar.json +150 -0
- package/dist/meta/SidebarItem.json +184 -0
- package/dist/meta/SidebarSection.json +131 -0
- package/dist/meta/Skeleton.json +120 -0
- package/dist/meta/Spinner.json +123 -0
- package/dist/meta/Stepper.json +135 -0
- package/dist/meta/StepperStep.json +148 -0
- package/dist/meta/Tabs.json +5 -0
- package/dist/meta/Toggle.json +18 -1
- package/dist/meta/ToolCall.json +192 -0
- package/dist/meta/Tooltip.json +3 -3
- package/dist/meta/components.json +6759 -2705
- package/dist/meta/contrast.json +11075 -3098
- package/dist/meta/index.json +266 -8
- package/dist/meta/patterns/AgentRun.json +27 -0
- package/dist/meta/patterns/Form.json +9 -0
- package/dist/meta/patterns/PageShell.json +43 -3
- package/dist/meta/patterns/Wizard.json +20 -1
- package/dist/meta/patterns/index.json +10 -1
- package/dist/styles/accordion.css +102 -0
- package/dist/styles/breadcrumb.css +72 -0
- package/dist/styles/combobox.css +154 -0
- package/dist/styles/date-picker.css +420 -0
- package/dist/styles/date-range-picker.css +280 -0
- package/dist/styles/drawer.css +181 -0
- package/dist/styles/empty-state.css +16 -4
- package/dist/styles/file-upload.css +236 -0
- package/dist/styles/header.css +26 -0
- package/dist/styles/index.css +23 -0
- package/dist/styles/number-input.css +83 -0
- package/dist/styles/progress-bar.css +107 -0
- package/dist/styles/prompt-input.css +127 -0
- package/dist/styles/segmented-control.css +182 -0
- package/dist/styles/sidebar.css +307 -0
- package/dist/styles/skeleton.css +76 -0
- package/dist/styles/spinner.css +70 -0
- package/dist/styles/stepper.css +250 -0
- package/dist/styles/tool-call.css +202 -0
- package/llms.txt +2 -2
- package/package.json +2 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Skeleton",
|
|
3
|
+
"source": "src/components/Skeleton.tsx",
|
|
4
|
+
"propsType": "SkeletonProps",
|
|
5
|
+
"description": "Skeleton — the shape of content that has not arrived.\n\nIT IS HIDDEN FROM ASSISTIVE TECHNOLOGY, ALWAYS, AND THAT IS THE WHOLE POINT\n\nA skeleton is a picture of content, and a screen reader cannot use a picture\nof content. Announcing it produces a stream of empty boxes between the user\nand the thing they asked for. So every skeleton is `aria-hidden`, with no way\nto opt out — a prop for that would only ever be used by mistake.\n\nWHICH MEANS THE CALLER OWES THE ANNOUNCEMENT, AND THIS COMPONENT CANNOT DO IT\n\nHiding the placeholder is only half an answer: something still has to tell a\nscreen-reader user that the region is loading. That belongs on the region\nbeing replaced — `aria-busy=\"true\"` while it loads — because only the caller\nknows where that region starts and ends. `Spinner` is the other half when the\nwait deserves an announcement of its own.\n\n <section aria-busy={isLoading}>\n {isLoading ? <Skeleton lines={3} /> : <Rows data={data} />}\n </section>\n\nThis is the one component in the system whose correct use REQUIRES something\nof the caller that the type system cannot check, which is why it is stated\nhere, in the contract, and in the story.",
|
|
6
|
+
"import": "import { Skeleton } from 'ionbase-ui';",
|
|
7
|
+
"status": "stable",
|
|
8
|
+
"summary": "The shape of content that has not arrived. Always hidden from assistive technology — the caller owns the announcement.",
|
|
9
|
+
"useWhen": [
|
|
10
|
+
"a region will be replaced by content whose shape is known in advance",
|
|
11
|
+
"the layout would otherwise jump when the content lands",
|
|
12
|
+
"several regions load independently and a single page spinner would hide that"
|
|
13
|
+
],
|
|
14
|
+
"useInstead": [
|
|
15
|
+
{
|
|
16
|
+
"when": "the shape of what is coming is not known",
|
|
17
|
+
"use": "Spinner",
|
|
18
|
+
"why": "a skeleton that does not match what replaces it is a worse promise than no promise"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"when": "the region is empty because there is nothing to show",
|
|
22
|
+
"use": "EmptyState",
|
|
23
|
+
"why": "a skeleton that never resolves is how an empty list looks like a broken one"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"variants": {
|
|
27
|
+
"variant": {
|
|
28
|
+
"text": {
|
|
29
|
+
"use": "lines of copy",
|
|
30
|
+
"note": "height comes from the line box, so the block occupies what the real paragraph will. With `lines`, the last is drawn short because real paragraphs end mid-measure"
|
|
31
|
+
},
|
|
32
|
+
"circle": {
|
|
33
|
+
"use": "an avatar or an icon slot"
|
|
34
|
+
},
|
|
35
|
+
"rect": {
|
|
36
|
+
"use": "a card, a thumbnail, a chart"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"a11y": {
|
|
41
|
+
"requires": [
|
|
42
|
+
"`aria-busy=\"true\"` on the region being replaced, set by the CALLER — this component cannot do it, because only the caller knows where the region starts and ends"
|
|
43
|
+
],
|
|
44
|
+
"notes": [
|
|
45
|
+
"every skeleton is aria-hidden with no opt-out: a screen reader cannot use a picture of content, and announcing it puts a stream of empty boxes between the user and what they asked for",
|
|
46
|
+
"prefers-reduced-motion stops the pulse outright, unlike Spinner and ProgressBar — a skeleton conveys nothing a static block does not, so there is no signal to preserve"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"antiPatterns": [
|
|
50
|
+
{
|
|
51
|
+
"dont": "<Skeleton /> inside a region with no aria-busy",
|
|
52
|
+
"do": "<section aria-busy={isLoading}>{isLoading ? <Skeleton lines={3} /> : <Rows />}</section>",
|
|
53
|
+
"why": "the skeleton is hidden, so without aria-busy a screen-reader user is told nothing at all — the region simply reads as empty"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"dont": "a skeleton whose shape does not match the content that replaces it",
|
|
57
|
+
"do": "match the line count and block sizes to the real thing",
|
|
58
|
+
"why": "the entire benefit is that the layout does not move; a mismatched skeleton pays the cost and keeps the jump"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"dont": "leaving skeletons on screen when a request fails",
|
|
62
|
+
"do": "render EmptyState with reason=\"error\"",
|
|
63
|
+
"why": "a skeleton that never resolves is indistinguishable from a hung page"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"stylesheet": "src/styles/skeleton.css",
|
|
67
|
+
"tokens": [
|
|
68
|
+
"--border-width-default",
|
|
69
|
+
"--icon-size-lg",
|
|
70
|
+
"--ion-duration-slow",
|
|
71
|
+
"--ion-ease-in-out",
|
|
72
|
+
"--ion-skeleton-height",
|
|
73
|
+
"--ion-skeleton-radius",
|
|
74
|
+
"--ion-skeleton-width",
|
|
75
|
+
"--radius-full",
|
|
76
|
+
"--radius-md",
|
|
77
|
+
"--radius-sm",
|
|
78
|
+
"--spacing-16",
|
|
79
|
+
"--spacing-64",
|
|
80
|
+
"--spacing-8",
|
|
81
|
+
"--surface-muted",
|
|
82
|
+
"--type-body-line-height"
|
|
83
|
+
],
|
|
84
|
+
"props": {
|
|
85
|
+
"variant": {
|
|
86
|
+
"type": "SkeletonVariant | undefined",
|
|
87
|
+
"required": false,
|
|
88
|
+
"origin": "own",
|
|
89
|
+
"values": [
|
|
90
|
+
"circle",
|
|
91
|
+
"text",
|
|
92
|
+
"rect"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"width": {
|
|
96
|
+
"type": "string | undefined",
|
|
97
|
+
"required": false,
|
|
98
|
+
"origin": "own",
|
|
99
|
+
"description": "Any CSS length. Defaults to filling the width available."
|
|
100
|
+
},
|
|
101
|
+
"height": {
|
|
102
|
+
"type": "string | undefined",
|
|
103
|
+
"required": false,
|
|
104
|
+
"origin": "own",
|
|
105
|
+
"description": "Any CSS length. `text` derives its height from the line box instead."
|
|
106
|
+
},
|
|
107
|
+
"lines": {
|
|
108
|
+
"type": "number | undefined",
|
|
109
|
+
"required": false,
|
|
110
|
+
"origin": "own",
|
|
111
|
+
"description": "`text` only: how many lines to draw. The last is drawn short."
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"propCounts": {
|
|
115
|
+
"own": 4,
|
|
116
|
+
"aria": 0,
|
|
117
|
+
"dom": 277,
|
|
118
|
+
"other": 0
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Spinner",
|
|
3
|
+
"source": "src/components/Spinner.tsx",
|
|
4
|
+
"propsType": "SpinnerProps",
|
|
5
|
+
"description": "Spinner — an indeterminate wait, announced once.\n\nWHY THIS IS NOT A DIV WITH A CSS ANIMATION\n\nThe obvious implementation is inaccessible in a specific and common way: a\nspinning graphic with no text is silence to a screen reader, so the user is\ntold nothing happened. Generated code in this repo's own eval corpus reached\nfor a bare `role=\"status\"` in 48 files, which is the right instinct and only\nhalf the job — the role creates a live region, but an empty one announces\nnothing at all.\n\nSo the default carries a label. It is visually hidden, it lives inside the\nlive region, and it is what makes \"Loading\" reach the user who cannot see the\nring.\n\n`isDecorative` exists because the opposite mistake is just as easy. A spinner\ninside a button whose label already changed to \"Saving…\" would announce\ntwice, and two live regions racing is worse than one. Decorative renders no\nrole at all rather than `aria-hidden` on a live region, which browsers treat\ninconsistently.\n\nMOTION\n\n`prefers-reduced-motion` slows the rotation rather than stopping it. A\nstationary spinner is not a calmer spinner — it is a broken one, and it\nremoves the only signal a sighted user has that the wait is still live.",
|
|
6
|
+
"import": "import { Spinner } from 'ionbase-ui';",
|
|
7
|
+
"status": "stable",
|
|
8
|
+
"summary": "An indeterminate wait, announced once. A ring plus a politely-announced label, sized off the icon ramp.",
|
|
9
|
+
"useWhen": [
|
|
10
|
+
"work is in flight and its duration cannot be measured",
|
|
11
|
+
"a control has started something and must show it is working",
|
|
12
|
+
"a small region is waiting and a skeleton would be more scaffolding than content"
|
|
13
|
+
],
|
|
14
|
+
"useInstead": [
|
|
15
|
+
{
|
|
16
|
+
"when": "the work has a measurable fraction complete",
|
|
17
|
+
"use": "ProgressBar",
|
|
18
|
+
"why": "a spinner over measurable work discards information the user wants — a spinner and a 90%-complete upload look identical"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"when": "a region is being replaced by content with a known shape",
|
|
22
|
+
"use": "Skeleton",
|
|
23
|
+
"why": "a skeleton holds the layout so it does not jump when content lands; a spinner leaves a hole that then reflows"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"variants": {
|
|
27
|
+
"size": {
|
|
28
|
+
"sm": {
|
|
29
|
+
"use": "inside a control, or beside a line of text"
|
|
30
|
+
},
|
|
31
|
+
"md": {
|
|
32
|
+
"use": "the default — a panel or a section"
|
|
33
|
+
},
|
|
34
|
+
"lg": {
|
|
35
|
+
"use": "a whole route or a full-page wait"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"a11y": {
|
|
40
|
+
"requires": [
|
|
41
|
+
"a `label` naming what is loading — it is announced politely and is the only thing a screen-reader user receives",
|
|
42
|
+
"`isDecorative` when something else already announces the wait, so two live regions do not race"
|
|
43
|
+
],
|
|
44
|
+
"notes": [
|
|
45
|
+
"the ring is aria-hidden; the label carries the meaning",
|
|
46
|
+
"prefers-reduced-motion slows the rotation rather than stopping it — a stationary spinner is indistinguishable from a hung one"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"antiPatterns": [
|
|
50
|
+
{
|
|
51
|
+
"dont": "<div className=\"spinner\" role=\"status\" />",
|
|
52
|
+
"do": "<Spinner label=\"Loading invoices\" />",
|
|
53
|
+
"why": "role=\"status\" creates a live region; an EMPTY live region announces nothing. This exact half-measure appeared in 48 files of this repo's eval corpus"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"dont": "<Button isDisabled>Saving… <Spinner /></Button>",
|
|
57
|
+
"do": "<Button isDisabled>Saving… <Spinner isDecorative /></Button>",
|
|
58
|
+
"why": "the button label already says it. Two live regions announcing the same wait talk over each other"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"dont": "a Spinner for an upload that reports bytes transferred",
|
|
62
|
+
"do": "ProgressBar with value",
|
|
63
|
+
"why": "throwing away a known percentage makes a finite wait feel unbounded"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"stylesheet": "src/styles/spinner.css",
|
|
67
|
+
"tokens": [
|
|
68
|
+
"--border-primary-strong",
|
|
69
|
+
"--border-subtle",
|
|
70
|
+
"--border-width-thick",
|
|
71
|
+
"--font-family-sans",
|
|
72
|
+
"--icon-size-lg",
|
|
73
|
+
"--icon-size-md",
|
|
74
|
+
"--icon-size-sm",
|
|
75
|
+
"--ion-duration-slow",
|
|
76
|
+
"--ion-ease-linear",
|
|
77
|
+
"--ion-spinner-head",
|
|
78
|
+
"--ion-spinner-size",
|
|
79
|
+
"--ion-spinner-track",
|
|
80
|
+
"--radius-full",
|
|
81
|
+
"--spacing-8",
|
|
82
|
+
"--text-secondary",
|
|
83
|
+
"--type-body-sm",
|
|
84
|
+
"--type-body-sm-line-height"
|
|
85
|
+
],
|
|
86
|
+
"props": {
|
|
87
|
+
"size": {
|
|
88
|
+
"type": "SpinnerSize | undefined",
|
|
89
|
+
"required": false,
|
|
90
|
+
"origin": "own",
|
|
91
|
+
"description": "Matches the icon size ramp: sm, md, lg.",
|
|
92
|
+
"values": [
|
|
93
|
+
"sm",
|
|
94
|
+
"md",
|
|
95
|
+
"lg"
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
"label": {
|
|
99
|
+
"type": "string | undefined",
|
|
100
|
+
"required": false,
|
|
101
|
+
"origin": "own",
|
|
102
|
+
"description": "What is loading. Announced politely; visible only with `isLabelVisible`."
|
|
103
|
+
},
|
|
104
|
+
"isLabelVisible": {
|
|
105
|
+
"type": "boolean | undefined",
|
|
106
|
+
"required": false,
|
|
107
|
+
"origin": "own",
|
|
108
|
+
"description": "Render the label as text beside the ring as well as announcing it."
|
|
109
|
+
},
|
|
110
|
+
"isDecorative": {
|
|
111
|
+
"type": "boolean | undefined",
|
|
112
|
+
"required": false,
|
|
113
|
+
"origin": "own",
|
|
114
|
+
"description": "The spinner conveys nothing on its own — something else already says the\nregion is busy. Renders inert: no role, no live region, hidden from AT."
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"propCounts": {
|
|
118
|
+
"own": 4,
|
|
119
|
+
"aria": 0,
|
|
120
|
+
"dom": 277,
|
|
121
|
+
"other": 0
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Stepper",
|
|
3
|
+
"source": "src/components/Stepper.tsx",
|
|
4
|
+
"propsType": "StepperProps",
|
|
5
|
+
"description": "Stepper — where the user is in a task that has an order.\n\nWHY NOT TABS\n\nThe `tablist` role announces peers that can be visited in any order, which is\nthe opposite of a wizard. This is an ordered list: the steps are a sequence,\nand `ol` says so. Nothing here is a tab, and arrow keys do nothing.\n\nPOSITION AND STATUS ARE TEXT, NOT ONLY DRAWING\n\nEvery step renders \"Step 2 of 5\" and its status as visually hidden text. The\nnumber in the circle is for the eye; a screen reader cannot be relied on to\nannounce list position, and a coloured ring carries nothing in forced-colours\nmode. The complete and error glyphs also differ in SHAPE — a check and an\nexclamation — so the states survive greyscale, which WCAG 1.4.1 requires.\n\nONLY VISITED STEPS ARE LINKS\n\n`href` / `onPress` are honoured on `complete` and `error` steps that are not\ncurrent. An `incomplete` step renders as text even when given one, because\njumping forward past unanswered steps is exactly what a wizard exists to\nprevent — and a link that is there but should not be followed is worse than\nno link. The current step is text for the same reason Breadcrumb's is: a link\nto where you already are is a dead control.\n\nIT DOES NOT ANNOUNCE STEP CHANGES\n\nThe Wizard pattern moves focus to the new step's heading, which announces it.\nA live region here as well would say the same thing twice, on every step.",
|
|
6
|
+
"import": "import { Stepper } from 'ionbase-ui';",
|
|
7
|
+
"status": "stable",
|
|
8
|
+
"summary": "Where the user is in a task that has an order. An ordered list of steps, each stating its position and status in text.",
|
|
9
|
+
"useWhen": [
|
|
10
|
+
"a Wizard — later steps depend on earlier answers and the user needs to see what is left",
|
|
11
|
+
"a multi-page flow such as checkout or onboarding, where the steps are fixed and known in advance"
|
|
12
|
+
],
|
|
13
|
+
"useInstead": [
|
|
14
|
+
{
|
|
15
|
+
"when": "the sections can be visited in any order",
|
|
16
|
+
"use": "Tabs",
|
|
17
|
+
"why": "tabs promise peers; a stepper promises a sequence, and using one for the other misleads in both directions"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"when": "the progress is a quantity with no named stages — an upload, an import",
|
|
21
|
+
"use": "ProgressBar",
|
|
22
|
+
"why": "a stepper with no meaningful step names is a progress bar drawn in circles"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"when": "the steps are an agent's actions rather than the user's",
|
|
26
|
+
"use": "AgentActivity",
|
|
27
|
+
"why": "the user is not moving through those steps; they are supervising them"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"variants": {
|
|
31
|
+
"orientation": {
|
|
32
|
+
"horizontal": {
|
|
33
|
+
"use": "the default — a header above the step's form. Below 40rem it keeps only the current label visible"
|
|
34
|
+
},
|
|
35
|
+
"vertical": {
|
|
36
|
+
"use": "a side rail beside the form, or when step names or descriptions are long"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"slots": {
|
|
41
|
+
"children": {
|
|
42
|
+
"accepts": "StepperStep",
|
|
43
|
+
"note": "first step first, and exactly one with isCurrent"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"a11y": {
|
|
47
|
+
"requires": [
|
|
48
|
+
"`label` naming the task when a page carries more than one stepper — it defaults to \"Progress\""
|
|
49
|
+
],
|
|
50
|
+
"guarantees": [
|
|
51
|
+
"every step renders \"Step N of M\" and its status as visually hidden text, derived from the children rather than passed in",
|
|
52
|
+
"the current step carries aria-current=\"step\"",
|
|
53
|
+
"connectors are CSS and never enter the accessibility tree"
|
|
54
|
+
],
|
|
55
|
+
"notes": [
|
|
56
|
+
"it does not announce step changes. The Wizard pattern moves focus to the new step's heading, which announces it; a live region here would say it twice"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"antiPatterns": [
|
|
60
|
+
{
|
|
61
|
+
"dont": "<Tabs> as a wizard's step indicator",
|
|
62
|
+
"do": "<Stepper>",
|
|
63
|
+
"why": "the tablist role announces peers that can be visited in any order, which is the opposite of a wizard"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"dont": "more than about seven steps",
|
|
67
|
+
"why": "a stepper that has to scroll or wrap no longer shows what is left, which is the only thing it is for — group the steps instead"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"stylesheet": "src/styles/stepper.css",
|
|
71
|
+
"tokens": [
|
|
72
|
+
"--border-default",
|
|
73
|
+
"--border-error",
|
|
74
|
+
"--border-focus",
|
|
75
|
+
"--border-primary",
|
|
76
|
+
"--border-strong",
|
|
77
|
+
"--border-width-thick",
|
|
78
|
+
"--border-width-thicker",
|
|
79
|
+
"--font-family-sans",
|
|
80
|
+
"--font-weight-medium",
|
|
81
|
+
"--font-weight-semibold",
|
|
82
|
+
"--icon-error",
|
|
83
|
+
"--icon-primary",
|
|
84
|
+
"--icon-size-lg",
|
|
85
|
+
"--icon-size-sm",
|
|
86
|
+
"--radius-full",
|
|
87
|
+
"--radius-xs",
|
|
88
|
+
"--spacing-16",
|
|
89
|
+
"--spacing-2",
|
|
90
|
+
"--spacing-24",
|
|
91
|
+
"--spacing-4",
|
|
92
|
+
"--spacing-8",
|
|
93
|
+
"--surface-error",
|
|
94
|
+
"--surface-primary",
|
|
95
|
+
"--text-default",
|
|
96
|
+
"--text-error",
|
|
97
|
+
"--text-on-color",
|
|
98
|
+
"--text-secondary",
|
|
99
|
+
"--text-tertiary",
|
|
100
|
+
"--type-body-sm",
|
|
101
|
+
"--type-body-sm-line-height",
|
|
102
|
+
"--type-caption",
|
|
103
|
+
"--type-caption-line-height"
|
|
104
|
+
],
|
|
105
|
+
"props": {
|
|
106
|
+
"label": {
|
|
107
|
+
"type": "string | undefined",
|
|
108
|
+
"required": false,
|
|
109
|
+
"origin": "own",
|
|
110
|
+
"description": "Names the list. Defaults to \"Progress\"; give it the task's name when a page\ncarries more than one — \"Checkout progress\"."
|
|
111
|
+
},
|
|
112
|
+
"orientation": {
|
|
113
|
+
"type": "StepperOrientation | undefined",
|
|
114
|
+
"required": false,
|
|
115
|
+
"origin": "own",
|
|
116
|
+
"description": "`horizontal` for a page-width header above the form, `vertical` for a side\nrail. Below 40rem a horizontal stepper hides every label but the current\none, so it does not need a second variant for mobile.",
|
|
117
|
+
"values": [
|
|
118
|
+
"horizontal",
|
|
119
|
+
"vertical"
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
"children": {
|
|
123
|
+
"type": "React.ReactNode",
|
|
124
|
+
"required": false,
|
|
125
|
+
"origin": "own",
|
|
126
|
+
"description": "`StepperStep` elements, first step first."
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"propCounts": {
|
|
130
|
+
"own": 3,
|
|
131
|
+
"aria": 0,
|
|
132
|
+
"dom": 277,
|
|
133
|
+
"other": 0
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "StepperStep",
|
|
3
|
+
"source": "src/components/Stepper.tsx",
|
|
4
|
+
"propsType": "StepperStepProps",
|
|
5
|
+
"import": "import { StepperStep } from 'ionbase-ui';",
|
|
6
|
+
"status": "stable",
|
|
7
|
+
"summary": "One step in a Stepper: a numbered indicator, a name, and a status carried by shape and text as well as colour.",
|
|
8
|
+
"useWhen": [
|
|
9
|
+
"inside a Stepper, once per step"
|
|
10
|
+
],
|
|
11
|
+
"useInstead": [
|
|
12
|
+
{
|
|
13
|
+
"when": "it is a step an agent is running",
|
|
14
|
+
"use": "AgentActivityStep"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"variants": {
|
|
18
|
+
"status": {
|
|
19
|
+
"incomplete": {
|
|
20
|
+
"use": "the default — not yet answered. Never a link, even with href"
|
|
21
|
+
},
|
|
22
|
+
"complete": {
|
|
23
|
+
"use": "answers accepted by the server, not merely filled in"
|
|
24
|
+
},
|
|
25
|
+
"error": {
|
|
26
|
+
"use": "the step's answers were rejected. Put the reason on the step's fields, not here"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"slots": {
|
|
31
|
+
"children": {
|
|
32
|
+
"accepts": "text",
|
|
33
|
+
"note": "the step's name, short — \"Shipping\", not \"Enter your shipping details\""
|
|
34
|
+
},
|
|
35
|
+
"description": {
|
|
36
|
+
"accepts": "text",
|
|
37
|
+
"note": "what the step asks for, or a summary of the answer once complete"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"a11y": {
|
|
41
|
+
"guarantees": [
|
|
42
|
+
"the current step and incomplete steps render as text, never as links, whatever href or onPress say",
|
|
43
|
+
"complete and error use different glyphs — a check and an exclamation — so status survives greyscale and forced-colours mode"
|
|
44
|
+
],
|
|
45
|
+
"notes": [
|
|
46
|
+
"status and isCurrent are separate on purpose: a user who goes Back to step 1 is on a step that is both current and complete"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"antiPatterns": [
|
|
50
|
+
{
|
|
51
|
+
"dont": "status=\"complete\" as soon as the fields are filled",
|
|
52
|
+
"do": "mark it complete when the server accepts the step",
|
|
53
|
+
"why": "a green check on answers that are later rejected sends the user back through a step they were told was finished"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"dont": "href on every step so users can jump ahead",
|
|
57
|
+
"why": "incomplete steps ignore it; skipping unanswered steps is what a wizard exists to prevent"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"dont": "using it outside Stepper",
|
|
61
|
+
"why": "it renders an <li> and reads its position from the Stepper, and throws without one"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"stylesheet": "src/styles/stepper.css",
|
|
65
|
+
"tokens": [
|
|
66
|
+
"--border-default",
|
|
67
|
+
"--border-error",
|
|
68
|
+
"--border-focus",
|
|
69
|
+
"--border-primary",
|
|
70
|
+
"--border-strong",
|
|
71
|
+
"--border-width-thick",
|
|
72
|
+
"--border-width-thicker",
|
|
73
|
+
"--font-family-sans",
|
|
74
|
+
"--font-weight-medium",
|
|
75
|
+
"--font-weight-semibold",
|
|
76
|
+
"--icon-error",
|
|
77
|
+
"--icon-primary",
|
|
78
|
+
"--icon-size-lg",
|
|
79
|
+
"--icon-size-sm",
|
|
80
|
+
"--radius-full",
|
|
81
|
+
"--radius-xs",
|
|
82
|
+
"--spacing-16",
|
|
83
|
+
"--spacing-2",
|
|
84
|
+
"--spacing-24",
|
|
85
|
+
"--spacing-4",
|
|
86
|
+
"--spacing-8",
|
|
87
|
+
"--surface-error",
|
|
88
|
+
"--surface-primary",
|
|
89
|
+
"--text-default",
|
|
90
|
+
"--text-error",
|
|
91
|
+
"--text-on-color",
|
|
92
|
+
"--text-secondary",
|
|
93
|
+
"--text-tertiary",
|
|
94
|
+
"--type-body-sm",
|
|
95
|
+
"--type-body-sm-line-height",
|
|
96
|
+
"--type-caption",
|
|
97
|
+
"--type-caption-line-height"
|
|
98
|
+
],
|
|
99
|
+
"props": {
|
|
100
|
+
"children": {
|
|
101
|
+
"type": "React.ReactNode",
|
|
102
|
+
"required": false,
|
|
103
|
+
"origin": "own",
|
|
104
|
+
"description": "The step's name, in the user's language."
|
|
105
|
+
},
|
|
106
|
+
"description": {
|
|
107
|
+
"type": "React.ReactNode",
|
|
108
|
+
"required": false,
|
|
109
|
+
"origin": "own",
|
|
110
|
+
"description": "A short second line — what the step asks for, or a summary of the answer."
|
|
111
|
+
},
|
|
112
|
+
"status": {
|
|
113
|
+
"type": "StepperStepStatus | undefined",
|
|
114
|
+
"required": false,
|
|
115
|
+
"origin": "own",
|
|
116
|
+
"description": "Whether the step's answers have been accepted. Orthogonal to `isCurrent`:\na user who goes Back to step 1 is on a step that is both current and\ncomplete.",
|
|
117
|
+
"values": [
|
|
118
|
+
"error",
|
|
119
|
+
"incomplete",
|
|
120
|
+
"complete"
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
"isCurrent": {
|
|
124
|
+
"type": "boolean | undefined",
|
|
125
|
+
"required": false,
|
|
126
|
+
"origin": "own",
|
|
127
|
+
"description": "The step the user is on. Marked `aria-current=\"step\"`."
|
|
128
|
+
},
|
|
129
|
+
"href": {
|
|
130
|
+
"type": "string | undefined",
|
|
131
|
+
"required": false,
|
|
132
|
+
"origin": "own",
|
|
133
|
+
"description": "Makes a visited step a link back to it. Ignored on the current step and on\n`incomplete` steps — see the component's comment for why."
|
|
134
|
+
},
|
|
135
|
+
"onPress": {
|
|
136
|
+
"type": "(() => void) | undefined",
|
|
137
|
+
"required": false,
|
|
138
|
+
"origin": "own",
|
|
139
|
+
"description": "As `href`, for a wizard that changes step without a route."
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"propCounts": {
|
|
143
|
+
"own": 6,
|
|
144
|
+
"aria": 0,
|
|
145
|
+
"dom": 277,
|
|
146
|
+
"other": 0
|
|
147
|
+
}
|
|
148
|
+
}
|
package/dist/meta/Tabs.json
CHANGED
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
"use": "Divider",
|
|
21
21
|
"why": "tabs hide everything but the active panel, including from Ctrl-F"
|
|
22
22
|
},
|
|
23
|
+
{
|
|
24
|
+
"when": "the options set a value — a view mode, a range, a unit — and no panel belongs to each",
|
|
25
|
+
"use": "SegmentedControl",
|
|
26
|
+
"why": "it looks like the pill type and is a radiogroup: a tab with no panel announces a relationship that does not exist"
|
|
27
|
+
},
|
|
23
28
|
{
|
|
24
29
|
"when": "it is navigation between pages rather than panels within one",
|
|
25
30
|
"use": "NavItem"
|
package/dist/meta/Toggle.json
CHANGED
|
@@ -72,6 +72,11 @@
|
|
|
72
72
|
{
|
|
73
73
|
"dont": "a button with `aria-pressed` for a setting",
|
|
74
74
|
"why": "a switch is a form value; the checkbox gives form association and `:checked` for free"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"dont": "<Toggle isSelected={on} onChange={(isSelected) => setOn(isSelected)} />",
|
|
78
|
+
"do": "<Toggle isSelected={on} onSelectionChange={setOn} />",
|
|
79
|
+
"why": "as Checkbox: `isSelected` pairs with `onSelectionChange` and `checked` pairs with `onChange(event)`. Toggle extends the input element, so `onChange` keeps the DOM signature it has always had."
|
|
75
80
|
}
|
|
76
81
|
],
|
|
77
82
|
"deprecated": [
|
|
@@ -167,10 +172,22 @@
|
|
|
167
172
|
"type": "React.ReactNode",
|
|
168
173
|
"required": false,
|
|
169
174
|
"origin": "own"
|
|
175
|
+
},
|
|
176
|
+
"isSelected": {
|
|
177
|
+
"type": "boolean | undefined",
|
|
178
|
+
"required": false,
|
|
179
|
+
"origin": "own",
|
|
180
|
+
"description": "React Aria's name for `checked`. Wins when both are passed."
|
|
181
|
+
},
|
|
182
|
+
"onSelectionChange": {
|
|
183
|
+
"type": "((isSelected: boolean) => void) | undefined",
|
|
184
|
+
"required": false,
|
|
185
|
+
"origin": "own",
|
|
186
|
+
"description": "Receives the new selection state rather than the change event."
|
|
170
187
|
}
|
|
171
188
|
},
|
|
172
189
|
"propCounts": {
|
|
173
|
-
"own":
|
|
190
|
+
"own": 7,
|
|
174
191
|
"aria": 0,
|
|
175
192
|
"dom": 304,
|
|
176
193
|
"other": 0
|