ionbase-ui 0.16.0 → 0.17.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.
Files changed (43) hide show
  1. package/README.md +34 -7
  2. package/dist/meta/Alert.json +248 -0
  3. package/dist/meta/Avatar.json +97 -0
  4. package/dist/meta/AvatarGroup.json +82 -0
  5. package/dist/meta/Badge.json +83 -0
  6. package/dist/meta/Button.json +518 -0
  7. package/dist/meta/Checkbox.json +113 -0
  8. package/dist/meta/Divider.json +30 -0
  9. package/dist/meta/FullCard.json +97 -0
  10. package/dist/meta/Header.json +91 -0
  11. package/dist/meta/Icon.json +35 -0
  12. package/dist/meta/Input.json +558 -0
  13. package/dist/meta/Link.json +239 -0
  14. package/dist/meta/Logo.json +46 -0
  15. package/dist/meta/LogoMark.json +41 -0
  16. package/dist/meta/Menu.json +50 -0
  17. package/dist/meta/MenuItem.json +75 -0
  18. package/dist/meta/Modal.json +241 -0
  19. package/dist/meta/NavItem.json +76 -0
  20. package/dist/meta/PhoneInput.json +472 -0
  21. package/dist/meta/Popover.json +124 -0
  22. package/dist/meta/Radio.json +104 -0
  23. package/dist/meta/RadioGroup.json +126 -0
  24. package/dist/meta/ScrollProgress.json +63 -0
  25. package/dist/meta/Select.json +195 -0
  26. package/dist/meta/TabItem.json +15 -0
  27. package/dist/meta/Table.json +135 -0
  28. package/dist/meta/TableBody.json +43 -0
  29. package/dist/meta/TableCell.json +105 -0
  30. package/dist/meta/TableHead.json +43 -0
  31. package/dist/meta/TableRow.json +56 -0
  32. package/dist/meta/Tabs.json +186 -0
  33. package/dist/meta/Toast.json +105 -0
  34. package/dist/meta/ToastProvider.json +75 -0
  35. package/dist/meta/Toggle.json +102 -0
  36. package/dist/meta/Tooltip.json +78 -0
  37. package/dist/meta/components.json +4757 -0
  38. package/dist/meta/index.json +479 -0
  39. package/dist/styles/tokens/base.css +25 -25
  40. package/dist/styles/tokens/theme-dark.css +11 -11
  41. package/dist/tokens/index.d.ts +8 -8
  42. package/dist/tokens/index.js +8 -8
  43. package/package.json +12 -6
package/README.md CHANGED
@@ -172,13 +172,40 @@ icon sit correctly inside a Button.
172
172
 
173
173
  ## Entry points
174
174
 
175
- | Import | What it is |
176
- | --------------------- | ---------------------------------------------- |
177
- | `ionbase-ui` | Components, `Icon`, and the `tokens` object |
178
- | `ionbase-ui/styles` | Everything: tokens + typography + components |
179
- | `ionbase-ui/styles/*` | One component stylesheet, e.g. `button.css` |
180
- | `ionbase-ui/tokens` | Token custom properties only, no component CSS |
181
- | `ionbase-ui/tokens/*` | One token layer, e.g. `theme-dark.css` |
175
+ | Import | What it is |
176
+ | ----------------------- | --------------------------------------------------- |
177
+ | `ionbase-ui` | Components, `Icon`, and the `tokens` object |
178
+ | `ionbase-ui/styles` | Everything: tokens + typography + components |
179
+ | `ionbase-ui/styles/*` | One component stylesheet, e.g. `button.css` |
180
+ | `ionbase-ui/tokens` | Token custom properties only, no component CSS |
181
+ | `ionbase-ui/tokens/*` | One token layer, e.g. `theme-dark.css` |
182
+ | `ionbase-ui/meta/index` | Component index — names, summaries, variants (12KB) |
183
+ | `ionbase-ui/meta` | Every component's full contract in one file (180KB) |
184
+ | `ionbase-ui/meta/*` | One component's contract, e.g. `Button.json` |
185
+
186
+ ## Machine-readable component contracts
187
+
188
+ Every component ships a JSON contract describing not just its API but when to
189
+ use it, what to use instead, and what tends to go wrong:
190
+
191
+ ```ts
192
+ import index from 'ionbase-ui/meta/index' with { type: 'json' };
193
+ import button from 'ionbase-ui/meta/Button.json' with { type: 'json' };
194
+ ```
195
+
196
+ Each contract carries `summary`, `useWhen`, `useInstead`, `variants` with
197
+ per-value guidance, `slots`, `a11y`, `antiPatterns`, `deprecated`, the generated
198
+ `props` table, and the tokens its stylesheet consumes.
199
+
200
+ This exists because the thing consuming a design system is increasingly a coding
201
+ agent rather than a person reading documentation. **Start from
202
+ `ionbase-ui/meta/index`** — 12KB, every component with its variants — then load
203
+ the single contract you need. Loading all of them costs 180KB to answer a
204
+ question the index already answered.
205
+
206
+ Six components carry full intent today (`Button`, `Input`, `Select`, `Modal`,
207
+ `Table`, `Alert`). The rest ship the generated API plus their source
208
+ documentation, and are being filled in.
182
209
 
183
210
  Token values are also available as JavaScript, as `var(--…)` references:
184
211
 
@@ -0,0 +1,248 @@
1
+ {
2
+ "name": "Alert",
3
+ "source": "src/components/Alert.tsx",
4
+ "propsType": "AlertProps",
5
+ "description": "Alert — Figma `Alert` (812:1902).\n\n`Intent` x `Emphasis` x `Layout`, with the parts as props rather than\nvariants. Intents match `Badge` exactly, so a status mapped to one can be\npassed to the other.\n\nROLE IS CHOSEN BY INTENT, NOT PASSED IN. `error` and `warning` render\n`role=\"alert\"`, which interrupts a screen reader; the rest render\n`role=\"status\"`, which waits for a pause. Getting this backwards is the\ncommon failure — a page of `role=\"alert\"` status messages talks over the\nuser, and an error announced as a status is missed.\n\nThe icon is decorative: it repeats what the intent's colour and copy already\nsay, so it is `aria-hidden` and the live region carries the text alone.",
6
+ "import": "import { Alert } from 'ionbase-ui';",
7
+ "status": "stable",
8
+ "since": "0.13.1",
9
+ "summary": "A persistent, in-page status message. Intent picks the ARIA role for you.",
10
+ "useWhen": [
11
+ "the message belongs to the page and should stay until the condition clears",
12
+ "form-level validation summaries, permission notices, degraded-state banners"
13
+ ],
14
+ "useInstead": [
15
+ {
16
+ "when": "the message is transient and self-dismissing",
17
+ "use": "Toast",
18
+ "why": "an Alert stays in the layout; a Toast does not"
19
+ },
20
+ {
21
+ "when": "it labels a value inline rather than telling the user something",
22
+ "use": "Badge",
23
+ "why": "Badge shares Alert's intent vocabulary exactly, so a status mapped to one passes to the other"
24
+ }
25
+ ],
26
+ "variants": {
27
+ "intent": {
28
+ "error": {
29
+ "use": "something failed and the user must act",
30
+ "aria": "role=alert — interrupts the screen reader"
31
+ },
32
+ "warning": {
33
+ "use": "something may fail or has degraded",
34
+ "aria": "role=alert — interrupts the screen reader"
35
+ },
36
+ "success": {
37
+ "use": "an operation completed",
38
+ "aria": "role=status"
39
+ },
40
+ "information": {
41
+ "use": "neutral context the user did not ask for",
42
+ "aria": "role=status"
43
+ },
44
+ "primary": {
45
+ "use": "product announcements and promotions",
46
+ "aria": "role=status"
47
+ },
48
+ "neutral": {
49
+ "use": "low-emphasis context",
50
+ "aria": "role=status"
51
+ }
52
+ },
53
+ "emphasis": {
54
+ "subtle": {
55
+ "use": "the default — tinted surface, readable in long pages"
56
+ },
57
+ "solid": {
58
+ "use": "reserve for a single high-stakes banner; it dominates the layout"
59
+ }
60
+ },
61
+ "layout": {
62
+ "inline": {
63
+ "use": "inside a form or card, in the flow of content"
64
+ },
65
+ "banner": {
66
+ "use": "full-bleed at the top of a page or region"
67
+ }
68
+ }
69
+ },
70
+ "a11y": {
71
+ "guarantees": [
72
+ "the role follows the intent — error and warning are role=alert, everything else role=status",
73
+ "the icon is aria-hidden, so the live region announces the text once and not the glyph"
74
+ ],
75
+ "requires": [
76
+ "`dismissLabel` when `onDismiss` is set and the surrounding copy does not name the action"
77
+ ],
78
+ "notes": [
79
+ "role=alert interrupts whatever the screen reader is saying; role=status waits for a pause. This is why the role is not a prop."
80
+ ]
81
+ },
82
+ "antiPatterns": [
83
+ {
84
+ "dont": "using intent=\"error\" for a message that is merely informational",
85
+ "why": "error renders role=alert, which interrupts the screen reader. A page of interrupting status messages talks over the user."
86
+ },
87
+ {
88
+ "dont": "using intent=\"information\" for a genuine failure",
89
+ "why": "role=status waits for a pause, so a real error can be missed entirely"
90
+ },
91
+ {
92
+ "dont": "rendering an Alert conditionally for a transient confirmation",
93
+ "do": "useToast",
94
+ "why": "an Alert that appears and disappears shifts the layout under the user"
95
+ }
96
+ ],
97
+ "stylesheet": "src/styles/alert.css",
98
+ "tokens": [
99
+ "--border-default",
100
+ "--border-error-strong",
101
+ "--border-error-subtle",
102
+ "--border-information-strong",
103
+ "--border-information-subtle",
104
+ "--border-inverse",
105
+ "--border-primary-strong",
106
+ "--border-primary-subtle",
107
+ "--border-success-strong",
108
+ "--border-success-subtle",
109
+ "--border-warning-strong",
110
+ "--border-warning-subtle",
111
+ "--border-width-default",
112
+ "--border-width-thick",
113
+ "--font-family-sans",
114
+ "--font-weight-medium",
115
+ "--font-weight-regular",
116
+ "--icon-error",
117
+ "--icon-information",
118
+ "--icon-on-color",
119
+ "--icon-primary",
120
+ "--icon-secondary",
121
+ "--icon-size-md",
122
+ "--icon-success",
123
+ "--icon-warning",
124
+ "--ion-alert-border",
125
+ "--ion-alert-icon",
126
+ "--ion-alert-surface",
127
+ "--ion-alert-text",
128
+ "--ion-duration-base",
129
+ "--ion-ease-out",
130
+ "--radius-md",
131
+ "--radius-sm",
132
+ "--ring-focus",
133
+ "--spacing-12",
134
+ "--spacing-16",
135
+ "--spacing-32",
136
+ "--spacing-8",
137
+ "--surface-error",
138
+ "--surface-error-subtle",
139
+ "--surface-hover",
140
+ "--surface-information",
141
+ "--surface-information-subtle",
142
+ "--surface-inverse",
143
+ "--surface-muted",
144
+ "--surface-primary",
145
+ "--surface-primary-subtle",
146
+ "--surface-success",
147
+ "--surface-success-subtle",
148
+ "--surface-warning",
149
+ "--surface-warning-subtle",
150
+ "--text-error",
151
+ "--text-information",
152
+ "--text-link",
153
+ "--text-on-color",
154
+ "--text-secondary",
155
+ "--text-success",
156
+ "--text-warning",
157
+ "--type-body",
158
+ "--type-body-line-height"
159
+ ],
160
+ "props": {
161
+ "intent": {
162
+ "type": "AlertIntent | undefined",
163
+ "required": false,
164
+ "origin": "own",
165
+ "description": "Matches the Figma `Intent` variant. Same vocabulary as `Badge`.",
166
+ "values": [
167
+ "neutral",
168
+ "primary",
169
+ "success",
170
+ "warning",
171
+ "error",
172
+ "information"
173
+ ],
174
+ "default": "information"
175
+ },
176
+ "emphasis": {
177
+ "type": "AlertEmphasis | undefined",
178
+ "required": false,
179
+ "origin": "own",
180
+ "description": "Matches the Figma `Emphasis` variant.",
181
+ "values": [
182
+ "subtle",
183
+ "solid"
184
+ ],
185
+ "default": "subtle"
186
+ },
187
+ "layout": {
188
+ "type": "AlertLayout | undefined",
189
+ "required": false,
190
+ "origin": "own",
191
+ "description": "Matches the Figma `Layout` variant.",
192
+ "values": [
193
+ "inline",
194
+ "banner"
195
+ ],
196
+ "default": "inline"
197
+ },
198
+ "title": {
199
+ "type": "React.ReactNode",
200
+ "required": false,
201
+ "origin": "own",
202
+ "description": "Optional heading above the message."
203
+ },
204
+ "hideIcon": {
205
+ "type": "boolean | undefined",
206
+ "required": false,
207
+ "origin": "own",
208
+ "description": "Hide the leading icon. Shown by default."
209
+ },
210
+ "icon": {
211
+ "type": "React.ReactNode",
212
+ "required": false,
213
+ "origin": "own",
214
+ "description": "Replace the intent's glyph."
215
+ },
216
+ "actions": {
217
+ "type": "React.ReactNode",
218
+ "required": false,
219
+ "origin": "own",
220
+ "description": "Action buttons, rendered under the message."
221
+ },
222
+ "onDismiss": {
223
+ "type": "(() => void) | undefined",
224
+ "required": false,
225
+ "origin": "own",
226
+ "description": "Called when the dismiss button is pressed. Omit for no dismiss button."
227
+ },
228
+ "dismissLabel": {
229
+ "type": "string | undefined",
230
+ "required": false,
231
+ "origin": "own",
232
+ "description": "Accessible name for the dismiss button.",
233
+ "default": "Dismiss"
234
+ },
235
+ "children": {
236
+ "type": "React.ReactNode",
237
+ "required": false,
238
+ "origin": "own",
239
+ "description": "The message."
240
+ }
241
+ },
242
+ "propCounts": {
243
+ "own": 10,
244
+ "aria": 0,
245
+ "dom": 276,
246
+ "other": 0
247
+ }
248
+ }
@@ -0,0 +1,97 @@
1
+ {
2
+ "name": "Avatar",
3
+ "source": "src/components/Avatar.tsx",
4
+ "propsType": "AvatarProps",
5
+ "description": "Avatar picks its content the way Figma's `Type` variant does, but by\nprecedence rather than by a prop: image, then initials, then icon. A caller\npassing `src` and `initials` gets the image with the initials as its alt\nfallback, which is what you want when the image 404s.\n\nA failed `src` falls through to initials (then icon) rather than leaving the\nbrowser's broken-image glyph — that is why initials exist alongside `src`.\n\nIt is a `<span>`, not a `<div>`, so it can sit inline beside text without the\ncaller fighting a block element.",
6
+ "import": "import { Avatar } from 'ionbase-ui';",
7
+ "stylesheet": "src/styles/avatar.css",
8
+ "tokens": [
9
+ "--border-strong",
10
+ "--border-width-default",
11
+ "--font-family-sans",
12
+ "--font-weight-medium",
13
+ "--icon-size-2xs",
14
+ "--icon-size-lg",
15
+ "--icon-size-md",
16
+ "--icon-size-sm",
17
+ "--icon-tertiary",
18
+ "--ion-avatar-font-size",
19
+ "--ion-avatar-group-overlap",
20
+ "--ion-avatar-icon",
21
+ "--ion-avatar-line-height",
22
+ "--ion-avatar-radius",
23
+ "--ion-avatar-size",
24
+ "--radius-full",
25
+ "--radius-md",
26
+ "--radius-sm",
27
+ "--radius-xs",
28
+ "--spacing-24",
29
+ "--spacing-32",
30
+ "--spacing-40",
31
+ "--spacing-48",
32
+ "--surface-muted",
33
+ "--text-tertiary",
34
+ "--type-body",
35
+ "--type-body-line-height",
36
+ "--type-body-sm",
37
+ "--type-body-sm-line-height",
38
+ "--type-caption",
39
+ "--type-caption-line-height",
40
+ "--type-h5",
41
+ "--type-h5-line-height"
42
+ ],
43
+ "props": {
44
+ "size": {
45
+ "type": "AvatarSize | undefined",
46
+ "required": false,
47
+ "origin": "own",
48
+ "description": "Matches the Figma `Size` variant: Mini 24, Small 32, Medium 40, Large 48.",
49
+ "values": [
50
+ "mini",
51
+ "sm",
52
+ "md",
53
+ "lg"
54
+ ]
55
+ },
56
+ "shape": {
57
+ "type": "AvatarShape | undefined",
58
+ "required": false,
59
+ "origin": "own",
60
+ "description": "Matches the Figma `Shape` variant.",
61
+ "values": [
62
+ "circle",
63
+ "square"
64
+ ]
65
+ },
66
+ "src": {
67
+ "type": "string | undefined",
68
+ "required": false,
69
+ "origin": "own",
70
+ "description": "Image source. Figma's `Type=Image`."
71
+ },
72
+ "alt": {
73
+ "type": "string | undefined",
74
+ "required": false,
75
+ "origin": "own",
76
+ "description": "Describes the person, not the picture. Falls back to `initials` so the\navatar is never announced as an unlabelled image."
77
+ },
78
+ "initials": {
79
+ "type": "string | undefined",
80
+ "required": false,
81
+ "origin": "own",
82
+ "description": "Figma's `Type=Character`. Rendered when there is no `src`, or when `src`\nfails to load."
83
+ },
84
+ "icon": {
85
+ "type": "React.ReactNode",
86
+ "required": false,
87
+ "origin": "own",
88
+ "description": "Figma's `Type=Icon`. Rendered when there is neither `src` nor `initials`."
89
+ }
90
+ },
91
+ "propCounts": {
92
+ "own": 6,
93
+ "aria": 0,
94
+ "dom": 278,
95
+ "other": 0
96
+ }
97
+ }
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "AvatarGroup",
3
+ "source": "src/components/Avatar.tsx",
4
+ "propsType": "AvatarGroupProps",
5
+ "description": "AvatarGroup overlaps its children by a quarter of the avatar size, matching\nFigma's -6 / -8 / -10 / -12 gaps at Mini / Small / Medium / Large.\n\nGroup defaults fill in `size` / `shape` only when a child Avatar has not set\nthem itself — an explicit child prop wins. Non-Avatar children are left\nalone so `size`/`shape` are never pushed onto arbitrary DOM nodes.\n\nChildren render in source order because that is Figma's stacking: each avatar\npaints over the one before it, so the `+N` overflow ends up on top. Later\nsiblings paint later, so this needs no z-index.",
6
+ "import": "import { AvatarGroup } from 'ionbase-ui';",
7
+ "stylesheet": "src/styles/avatar.css",
8
+ "tokens": [
9
+ "--border-strong",
10
+ "--border-width-default",
11
+ "--font-family-sans",
12
+ "--font-weight-medium",
13
+ "--icon-size-2xs",
14
+ "--icon-size-lg",
15
+ "--icon-size-md",
16
+ "--icon-size-sm",
17
+ "--icon-tertiary",
18
+ "--ion-avatar-font-size",
19
+ "--ion-avatar-group-overlap",
20
+ "--ion-avatar-icon",
21
+ "--ion-avatar-line-height",
22
+ "--ion-avatar-radius",
23
+ "--ion-avatar-size",
24
+ "--radius-full",
25
+ "--radius-md",
26
+ "--radius-sm",
27
+ "--radius-xs",
28
+ "--spacing-24",
29
+ "--spacing-32",
30
+ "--spacing-40",
31
+ "--spacing-48",
32
+ "--surface-muted",
33
+ "--text-tertiary",
34
+ "--type-body",
35
+ "--type-body-line-height",
36
+ "--type-body-sm",
37
+ "--type-body-sm-line-height",
38
+ "--type-caption",
39
+ "--type-caption-line-height",
40
+ "--type-h5",
41
+ "--type-h5-line-height"
42
+ ],
43
+ "props": {
44
+ "size": {
45
+ "type": "AvatarSize | undefined",
46
+ "required": false,
47
+ "origin": "own",
48
+ "values": [
49
+ "mini",
50
+ "sm",
51
+ "md",
52
+ "lg"
53
+ ]
54
+ },
55
+ "max": {
56
+ "type": "number | undefined",
57
+ "required": false,
58
+ "origin": "own",
59
+ "description": "Cap on avatars shown; the remainder becomes a `+N` overflow avatar."
60
+ },
61
+ "shape": {
62
+ "type": "AvatarShape | undefined",
63
+ "required": false,
64
+ "origin": "own",
65
+ "values": [
66
+ "circle",
67
+ "square"
68
+ ]
69
+ },
70
+ "children": {
71
+ "type": "React.ReactNode",
72
+ "required": false,
73
+ "origin": "own"
74
+ }
75
+ },
76
+ "propCounts": {
77
+ "own": 4,
78
+ "aria": 0,
79
+ "dom": 277,
80
+ "other": 0
81
+ }
82
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "Badge",
3
+ "source": "src/components/Badge.tsx",
4
+ "propsType": "BadgeProps",
5
+ "description": "Badge is presentational — no role, no interaction. React Aria has nothing to\noffer here, so this is a plain span rather than a hook wrapper for its own\nsake.\n\nIt carries no `status` or `alert` role on purpose: a badge is a label on\nsomething else, and announcing it as a live region would interrupt screen\nreader users on every render. Wrap it yourself if the value genuinely changes\nand matters.",
6
+ "import": "import { Badge } from 'ionbase-ui';",
7
+ "stylesheet": "src/styles/badge.css",
8
+ "tokens": [
9
+ "--border-default",
10
+ "--border-error-subtle",
11
+ "--border-information-subtle",
12
+ "--border-primary-subtle",
13
+ "--border-success-subtle",
14
+ "--border-warning-subtle",
15
+ "--border-width-default",
16
+ "--font-family-sans",
17
+ "--font-weight-medium",
18
+ "--icon-error",
19
+ "--icon-information",
20
+ "--icon-primary",
21
+ "--icon-secondary",
22
+ "--icon-success",
23
+ "--icon-warning",
24
+ "--radius-full",
25
+ "--spacing-2",
26
+ "--spacing-4",
27
+ "--spacing-6",
28
+ "--spacing-8",
29
+ "--surface-error-subtle",
30
+ "--surface-information-subtle",
31
+ "--surface-muted",
32
+ "--surface-primary-subtle",
33
+ "--surface-success-subtle",
34
+ "--surface-warning-subtle",
35
+ "--text-error",
36
+ "--text-information",
37
+ "--text-link",
38
+ "--text-secondary",
39
+ "--text-success",
40
+ "--text-warning",
41
+ "--type-caption",
42
+ "--type-caption-line-height"
43
+ ],
44
+ "props": {
45
+ "intent": {
46
+ "type": "BadgeIntent | undefined",
47
+ "required": false,
48
+ "origin": "own",
49
+ "description": "Matches the Figma `Intent` variant.",
50
+ "values": [
51
+ "neutral",
52
+ "primary",
53
+ "success",
54
+ "warning",
55
+ "error",
56
+ "information"
57
+ ]
58
+ },
59
+ "dot": {
60
+ "type": "boolean | undefined",
61
+ "required": false,
62
+ "origin": "own",
63
+ "description": "Show the leading dot marker. Figma's `Show Dot` boolean.\nThe dot inherits the intent's foreground colour, so it needs no prop."
64
+ },
65
+ "icon": {
66
+ "type": "React.ReactNode",
67
+ "required": false,
68
+ "origin": "own",
69
+ "description": "Leading icon. Figma's `Show Icon` + `Icon` swap. Mutually exclusive with `dot`."
70
+ },
71
+ "children": {
72
+ "type": "React.ReactNode",
73
+ "required": false,
74
+ "origin": "own"
75
+ }
76
+ },
77
+ "propCounts": {
78
+ "own": 4,
79
+ "aria": 0,
80
+ "dom": 277,
81
+ "other": 0
82
+ }
83
+ }