ionbase-ui 0.16.0 → 0.18.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 (53) hide show
  1. package/README.md +71 -7
  2. package/dist/meta/Alert.json +269 -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 +556 -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 +4816 -0
  38. package/dist/meta/contrast.json +3608 -0
  39. package/dist/meta/index.json +479 -0
  40. package/dist/styles/alert.css +10 -5
  41. package/dist/styles/tokens/base.css +25 -25
  42. package/dist/styles/tokens/theme-dark.css +11 -11
  43. package/dist/tokens/index.d.ts +8 -8
  44. package/dist/tokens/index.js +8 -8
  45. package/eslint-plugin/index.js +64 -0
  46. package/eslint-plugin/meta-data.js +66 -0
  47. package/eslint-plugin/rules/needs-accessible-name.js +88 -0
  48. package/eslint-plugin/rules/no-deprecated-props.js +74 -0
  49. package/eslint-plugin/rules/no-known-contrast-failure.js +75 -0
  50. package/eslint-plugin/rules/no-raw-style-values.js +107 -0
  51. package/eslint-plugin/rules/one-primary-action.js +128 -0
  52. package/package.json +33 -7
  53. package/stylelint-config.js +144 -0
@@ -0,0 +1,104 @@
1
+ {
2
+ "name": "Radio",
3
+ "source": "src/components/Radio.tsx",
4
+ "propsType": "RadioProps",
5
+ "import": "import { Radio } from 'ionbase-ui';",
6
+ "stylesheet": "src/styles/radio.css",
7
+ "tokens": [
8
+ "--border-disabled",
9
+ "--border-error-strong",
10
+ "--border-focus",
11
+ "--border-inverse",
12
+ "--border-primary-strong",
13
+ "--border-stronger",
14
+ "--border-width-default",
15
+ "--border-width-thick",
16
+ "--font-family-sans",
17
+ "--font-weight-medium",
18
+ "--icon-disabled",
19
+ "--icon-on-color",
20
+ "--ion-duration-base",
21
+ "--ion-ease-out",
22
+ "--ion-radio-border",
23
+ "--ion-radio-dot",
24
+ "--ion-radio-fill",
25
+ "--ion-radio-font-size",
26
+ "--ion-radio-gap",
27
+ "--ion-radio-line-height",
28
+ "--ion-radio-raised",
29
+ "--ion-radio-size",
30
+ "--ion-shadow-raised-flush-lg",
31
+ "--ion-shadow-raised-flush-sm",
32
+ "--ion-shadow-raised-flush-xs",
33
+ "--radius-full",
34
+ "--spacing-12",
35
+ "--spacing-16",
36
+ "--spacing-2",
37
+ "--spacing-20",
38
+ "--spacing-24",
39
+ "--spacing-8",
40
+ "--surface-default",
41
+ "--surface-disabled",
42
+ "--surface-error",
43
+ "--surface-inverse",
44
+ "--surface-primary",
45
+ "--text-disabled",
46
+ "--text-secondary",
47
+ "--type-body",
48
+ "--type-body-line-height",
49
+ "--type-body-sm",
50
+ "--type-body-sm-line-height"
51
+ ],
52
+ "props": {
53
+ "value": {
54
+ "type": "string",
55
+ "required": true,
56
+ "origin": "own"
57
+ },
58
+ "size": {
59
+ "type": "RadioSize | undefined",
60
+ "required": false,
61
+ "origin": "own",
62
+ "values": [
63
+ "sm",
64
+ "md",
65
+ "lg"
66
+ ]
67
+ },
68
+ "intent": {
69
+ "type": "RadioIntent | undefined",
70
+ "required": false,
71
+ "origin": "own",
72
+ "values": [
73
+ "neutral",
74
+ "brand",
75
+ "danger"
76
+ ]
77
+ },
78
+ "isDisabled": {
79
+ "type": "boolean | undefined",
80
+ "required": false,
81
+ "origin": "own",
82
+ "description": "Whether this radio is disabled. Falls back to the group's `isDisabled`."
83
+ },
84
+ "disabled": {
85
+ "type": "boolean | undefined",
86
+ "required": false,
87
+ "origin": "own",
88
+ "tags": {
89
+ "deprecated": "Use `isDisabled`. Accepted as an alias for one minor version."
90
+ }
91
+ },
92
+ "children": {
93
+ "type": "React.ReactNode",
94
+ "required": false,
95
+ "origin": "own"
96
+ }
97
+ },
98
+ "propCounts": {
99
+ "own": 6,
100
+ "aria": 0,
101
+ "dom": 303,
102
+ "other": 0
103
+ }
104
+ }
@@ -0,0 +1,126 @@
1
+ {
2
+ "name": "RadioGroup",
3
+ "source": "src/components/Radio.tsx",
4
+ "propsType": "RadioGroupProps",
5
+ "description": "Renders a `<fieldset>` with a `<legend>` rather than a div with\n`role=\"radiogroup\"`. Both are announced correctly, but a fieldset also groups\nthe inputs for form submission and native validation, which the ARIA version\ndoes not.",
6
+ "import": "import { RadioGroup } from 'ionbase-ui';",
7
+ "stylesheet": "src/styles/radio.css",
8
+ "tokens": [
9
+ "--border-disabled",
10
+ "--border-error-strong",
11
+ "--border-focus",
12
+ "--border-inverse",
13
+ "--border-primary-strong",
14
+ "--border-stronger",
15
+ "--border-width-default",
16
+ "--border-width-thick",
17
+ "--font-family-sans",
18
+ "--font-weight-medium",
19
+ "--icon-disabled",
20
+ "--icon-on-color",
21
+ "--ion-duration-base",
22
+ "--ion-ease-out",
23
+ "--ion-radio-border",
24
+ "--ion-radio-dot",
25
+ "--ion-radio-fill",
26
+ "--ion-radio-font-size",
27
+ "--ion-radio-gap",
28
+ "--ion-radio-line-height",
29
+ "--ion-radio-raised",
30
+ "--ion-radio-size",
31
+ "--ion-shadow-raised-flush-lg",
32
+ "--ion-shadow-raised-flush-sm",
33
+ "--ion-shadow-raised-flush-xs",
34
+ "--radius-full",
35
+ "--spacing-12",
36
+ "--spacing-16",
37
+ "--spacing-2",
38
+ "--spacing-20",
39
+ "--spacing-24",
40
+ "--spacing-8",
41
+ "--surface-default",
42
+ "--surface-disabled",
43
+ "--surface-error",
44
+ "--surface-inverse",
45
+ "--surface-primary",
46
+ "--text-disabled",
47
+ "--text-secondary",
48
+ "--type-body",
49
+ "--type-body-line-height",
50
+ "--type-body-sm",
51
+ "--type-body-sm-line-height"
52
+ ],
53
+ "props": {
54
+ "name": {
55
+ "type": "string | undefined",
56
+ "required": false,
57
+ "origin": "own",
58
+ "description": "Shared input name. Generated when omitted."
59
+ },
60
+ "value": {
61
+ "type": "string | undefined",
62
+ "required": false,
63
+ "origin": "own"
64
+ },
65
+ "defaultValue": {
66
+ "type": "string | undefined",
67
+ "required": false,
68
+ "origin": "own"
69
+ },
70
+ "onChange": {
71
+ "type": "((value: string) => void) | undefined",
72
+ "required": false,
73
+ "origin": "own"
74
+ },
75
+ "label": {
76
+ "type": "React.ReactNode",
77
+ "required": false,
78
+ "origin": "own"
79
+ },
80
+ "size": {
81
+ "type": "RadioSize | undefined",
82
+ "required": false,
83
+ "origin": "own",
84
+ "values": [
85
+ "sm",
86
+ "md",
87
+ "lg"
88
+ ]
89
+ },
90
+ "intent": {
91
+ "type": "RadioIntent | undefined",
92
+ "required": false,
93
+ "origin": "own",
94
+ "values": [
95
+ "neutral",
96
+ "brand",
97
+ "danger"
98
+ ]
99
+ },
100
+ "isDisabled": {
101
+ "type": "boolean | undefined",
102
+ "required": false,
103
+ "origin": "own",
104
+ "description": "Whether every radio in the group is disabled."
105
+ },
106
+ "disabled": {
107
+ "type": "boolean | undefined",
108
+ "required": false,
109
+ "origin": "own",
110
+ "tags": {
111
+ "deprecated": "Use `isDisabled`. Accepted as an alias for one minor version."
112
+ }
113
+ },
114
+ "children": {
115
+ "type": "React.ReactNode",
116
+ "required": false,
117
+ "origin": "own"
118
+ }
119
+ },
120
+ "propCounts": {
121
+ "own": 10,
122
+ "aria": 0,
123
+ "dom": 276,
124
+ "other": 0
125
+ }
126
+ }
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "ScrollProgress",
3
+ "source": "src/components/ScrollProgress.tsx",
4
+ "propsType": "ScrollProgressProps",
5
+ "description": "The compact rail is the disclosure trigger. It is a real `<button>`, so a\nkeyboard user reaches it by Tab and opens it the same way as any button —\nEnter or Space — no `:focus-within` trick required; a mouse user can also\njust hover it. Built as a WAI-ARIA Disclosure (`aria-expanded` +\n`aria-controls`) rather than a menu: see the CSS header for why\n`role=\"menu\"` is deliberately not used here.\n\nCloses on outside pointerdown and on Escape — the minimum a disclosure\nneeds to not trap the page once opened, not a full popover/focus-trap\nimplementation.",
6
+ "import": "import { ScrollProgress } from 'ionbase-ui';",
7
+ "stylesheet": "src/styles/scroll-progress.css",
8
+ "tokens": [
9
+ "--border-default",
10
+ "--border-focus",
11
+ "--border-strong",
12
+ "--border-stronger",
13
+ "--border-width-default",
14
+ "--border-width-thick",
15
+ "--font-family-sans",
16
+ "--font-weight-regular",
17
+ "--ion-duration-base",
18
+ "--ion-ease-out",
19
+ "--radius-md",
20
+ "--radius-sm",
21
+ "--spacing-12",
22
+ "--spacing-2",
23
+ "--spacing-32",
24
+ "--spacing-8",
25
+ "--surface-default",
26
+ "--surface-muted",
27
+ "--text-secondary",
28
+ "--text-tertiary",
29
+ "--type-body-sm",
30
+ "--type-body-sm-line-height"
31
+ ],
32
+ "props": {
33
+ "progress": {
34
+ "type": "number",
35
+ "required": true,
36
+ "origin": "own",
37
+ "description": "0-100. The caller computes this from whatever it is tracking — a\nwindow scroll fraction, a custom scroll container, a reading-time\nestimate. This component has no opinion on the source."
38
+ },
39
+ "sections": {
40
+ "type": "ScrollProgressSection[]",
41
+ "required": true,
42
+ "origin": "own"
43
+ },
44
+ "activeId": {
45
+ "type": "string | undefined",
46
+ "required": false,
47
+ "origin": "own",
48
+ "description": "The section the rail's heavier tick and the panel's selected row point\nto. Also the caller's to compute — see `progress`."
49
+ },
50
+ "onSelect": {
51
+ "type": "((id: string) => void) | undefined",
52
+ "required": false,
53
+ "origin": "own",
54
+ "description": "Fired when a row is chosen. The component does not scroll anywhere\nitself; matching Menu, it reports the choice and lets the caller decide\nwhat \"select this section\" means for their page."
55
+ }
56
+ },
57
+ "propCounts": {
58
+ "own": 4,
59
+ "aria": 0,
60
+ "dom": 277,
61
+ "other": 0
62
+ }
63
+ }
@@ -0,0 +1,195 @@
1
+ {
2
+ "name": "Select",
3
+ "source": "src/components/Select.tsx",
4
+ "propsType": "SelectProps",
5
+ "description": "Select wraps a native `<select>` rather than rebuilding one.\n\nReact Aria's `useSelect` builds a listbox in a popover, which is the right\nanswer when the menu needs custom rows — but Figma models only the trigger\nhere, and the dropdown list is a separate component on the Menu page. Until\nthat is built, the native control is the honest match: it brings keyboard\nhandling, type-ahead and the platform picker on mobile for free, and it is\naccessible without any of it being reimplemented.\n\nFigma's six states map the same way Input's do — Hover and Focus from\ninteraction, Filled from whether a value is set, the rest from props.",
6
+ "import": "import { Select } from 'ionbase-ui';",
7
+ "status": "stable",
8
+ "summary": "A dropdown over a native <select>, with the same label and error wiring as Input.",
9
+ "useWhen": [
10
+ "the user picks exactly one value from a known, modest list",
11
+ "the native platform picker is acceptable — and on mobile it usually is the best option"
12
+ ],
13
+ "useInstead": [
14
+ {
15
+ "when": "the list is long enough to need search or type-ahead beyond the native behaviour",
16
+ "use": "a combobox — not yet in this system"
17
+ },
18
+ {
19
+ "when": "the rows need icons, avatars or two lines of text",
20
+ "use": "Menu",
21
+ "why": "Select wraps a native <select>, whose options can only be text"
22
+ },
23
+ {
24
+ "when": "there are two or three mutually exclusive choices worth showing at once",
25
+ "use": "RadioGroup"
26
+ },
27
+ {
28
+ "when": "the user may pick more than one",
29
+ "use": "Checkbox"
30
+ }
31
+ ],
32
+ "variants": {
33
+ "size": {
34
+ "sm": {
35
+ "use": "dense forms, table filters"
36
+ },
37
+ "md": {
38
+ "use": "the default"
39
+ },
40
+ "lg": {
41
+ "use": "prominent single-field forms"
42
+ }
43
+ }
44
+ },
45
+ "a11y": {
46
+ "guarantees": [
47
+ "keyboard handling, type-ahead and the mobile platform picker come from the native <select> rather than being reimplemented",
48
+ "label, helper and error wiring match Input"
49
+ ],
50
+ "requires": [
51
+ "`label`, or `aria-label` when no visible label is rendered"
52
+ ]
53
+ },
54
+ "antiPatterns": [
55
+ {
56
+ "dont": "passing rich React nodes as option content",
57
+ "why": "this renders a native <select>; options are text only"
58
+ },
59
+ {
60
+ "dont": "using a Select with two options for an on/off setting",
61
+ "do": "Toggle",
62
+ "why": "a two-item dropdown costs two interactions to do what one control does in one"
63
+ }
64
+ ],
65
+ "deprecated": [
66
+ {
67
+ "prop": "disabled",
68
+ "replacement": "isDisabled"
69
+ }
70
+ ],
71
+ "stylesheet": "src/styles/select.css",
72
+ "tokens": [
73
+ "--border-default",
74
+ "--border-disabled",
75
+ "--border-error-strong",
76
+ "--border-focus",
77
+ "--border-strong",
78
+ "--border-width-default",
79
+ "--border-width-thick",
80
+ "--font-family-sans",
81
+ "--font-weight-regular",
82
+ "--icon-disabled",
83
+ "--icon-size-md",
84
+ "--icon-size-sm",
85
+ "--icon-tertiary",
86
+ "--ion-duration-base",
87
+ "--ion-ease-out",
88
+ "--ion-select-border-width",
89
+ "--ion-select-font-size",
90
+ "--ion-select-gap",
91
+ "--ion-select-height",
92
+ "--ion-select-icon-size",
93
+ "--ion-select-line-height",
94
+ "--ion-select-padding-x",
95
+ "--ion-select-radius",
96
+ "--radius-md",
97
+ "--radius-sm",
98
+ "--spacing-12",
99
+ "--spacing-16",
100
+ "--spacing-32",
101
+ "--spacing-40",
102
+ "--spacing-48",
103
+ "--spacing-6",
104
+ "--spacing-8",
105
+ "--surface-default",
106
+ "--surface-disabled",
107
+ "--text-disabled",
108
+ "--text-secondary",
109
+ "--text-tertiary",
110
+ "--type-body",
111
+ "--type-body-line-height",
112
+ "--type-body-sm",
113
+ "--type-body-sm-line-height"
114
+ ],
115
+ "props": {
116
+ "size": {
117
+ "type": "SelectSize | undefined",
118
+ "required": false,
119
+ "origin": "own",
120
+ "description": "Matches the Figma `Size` variant: Small, Medium, Large.",
121
+ "values": [
122
+ "sm",
123
+ "md",
124
+ "lg"
125
+ ],
126
+ "default": "md"
127
+ },
128
+ "options": {
129
+ "type": "SelectOption[] | undefined",
130
+ "required": false,
131
+ "origin": "own",
132
+ "description": "The options to render. Omit and pass `children` for grouped options."
133
+ },
134
+ "placeholder": {
135
+ "type": "string | undefined",
136
+ "required": false,
137
+ "origin": "own",
138
+ "description": "Text shown when nothing is selected. Renders as a disabled option so the\nfield can start empty without that being a valid choice."
139
+ },
140
+ "label": {
141
+ "type": "React.ReactNode",
142
+ "required": false,
143
+ "origin": "own",
144
+ "description": "Field label. Renders the Figma `Form Field` wrapper around the control."
145
+ },
146
+ "description": {
147
+ "type": "React.ReactNode",
148
+ "required": false,
149
+ "origin": "own",
150
+ "description": "Helper text below the field."
151
+ },
152
+ "errorMessage": {
153
+ "type": "React.ReactNode",
154
+ "required": false,
155
+ "origin": "own",
156
+ "description": "Replaces the helper text when `isInvalid` is set."
157
+ },
158
+ "isInvalid": {
159
+ "type": "boolean | undefined",
160
+ "required": false,
161
+ "origin": "own"
162
+ },
163
+ "isDisabled": {
164
+ "type": "boolean | undefined",
165
+ "required": false,
166
+ "origin": "own"
167
+ },
168
+ "disabled": {
169
+ "type": "boolean | undefined",
170
+ "required": false,
171
+ "origin": "own",
172
+ "tags": {
173
+ "deprecated": "Use `isDisabled`. Accepted as an alias for one minor version."
174
+ }
175
+ },
176
+ "className": {
177
+ "type": "string | undefined",
178
+ "required": false,
179
+ "origin": "own",
180
+ "description": "Class names for the control box (`.ion-select`). Always lands here —\nnever on the form-field wrapper. Use `wrapperClassName` for that."
181
+ },
182
+ "wrapperClassName": {
183
+ "type": "string | undefined",
184
+ "required": false,
185
+ "origin": "own",
186
+ "description": "Class names for the `.ion-field` wrapper when a label or helper is shown."
187
+ }
188
+ },
189
+ "propCounts": {
190
+ "own": 11,
191
+ "aria": 0,
192
+ "dom": 283,
193
+ "other": 0
194
+ }
195
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "TabItem",
3
+ "source": "../../node_modules/.pnpm/react-stately@3.48.0_react@19.2.8/node_modules/react-stately/dist/types/src/collections/Item.d.ts",
4
+ "propsType": null,
5
+ "import": "import { TabItem } from 'ionbase-ui';",
6
+ "stylesheet": null,
7
+ "tokens": [],
8
+ "props": {},
9
+ "propCounts": {
10
+ "own": 0,
11
+ "aria": 0,
12
+ "dom": 0,
13
+ "other": 0
14
+ }
15
+ }
@@ -0,0 +1,135 @@
1
+ {
2
+ "name": "Table",
3
+ "source": "src/components/Table.tsx",
4
+ "propsType": "TableProps",
5
+ "description": "`Table` owns the scroll container and the density class; everything else\nis composed from `TableHead` / `TableBody` / `TableRow` / `TableCell`,\nmatching how Figma actually layers `Table Row` > `Table Cell` > `Cell Text`\nrather than one component with a wall of props.\n\nDensity has no React state to thread: it only ever changes vertical cell\npadding, which `.ion-table--compact td` and friends apply through the CSS\ncascade from this one class. A context provider for a value nothing in JS\never reads would be infrastructure with no consumer.\n\nThe scroll container is a keyboard-reachable region (WCAG 2.1.1): overflow\nalone is not enough. `aria-label` / `aria-labelledby` on `Table` name that\nregion — the container accepts no props of its own, so the table's name is\nthe only place the label can live. Prefer a `<caption>` via\n`aria-labelledby` when the name should also be visible.",
6
+ "import": "import { Table } from 'ionbase-ui';",
7
+ "status": "stable",
8
+ "summary": "A data table. Table owns the scroll container and density; rows and cells are composed.",
9
+ "useWhen": [
10
+ "showing records with more than one attribute worth comparing across rows"
11
+ ],
12
+ "useInstead": [
13
+ {
14
+ "when": "each item is a self-contained summary rather than a row of fields",
15
+ "use": "FullCard"
16
+ },
17
+ {
18
+ "when": "it is a layout grid rather than data",
19
+ "use": "CSS grid",
20
+ "why": "a <table> announces row and column semantics that a layout does not have"
21
+ }
22
+ ],
23
+ "composition": {
24
+ "order": [
25
+ "Table",
26
+ "TableHead",
27
+ "TableRow",
28
+ "TableCell"
29
+ ],
30
+ "note": "Table > TableHead/TableBody > TableRow > TableCell, matching how Figma layers Table Row > Table Cell > Cell Text. TableHead and TableBody take no props of their own; they place the rows and give TableCell its head-or-body context.",
31
+ "example": "<Table aria-label=\"Users\"><TableHead><TableRow><TableCell scope=\"col\">Name</TableCell></TableRow></TableHead><TableBody><TableRow><TableCell>Ada</TableCell></TableRow></TableBody></Table>"
32
+ },
33
+ "variants": {
34
+ "density": {
35
+ "compact": {
36
+ "use": "dense operational tables where row count matters more than comfort"
37
+ },
38
+ "default": {
39
+ "use": "the default"
40
+ },
41
+ "relaxed": {
42
+ "use": "short tables where each row deserves attention"
43
+ }
44
+ }
45
+ },
46
+ "a11y": {
47
+ "guarantees": [
48
+ "the scroll container is keyboard reachable, which overflow alone does not provide (WCAG 2.1.1)"
49
+ ],
50
+ "requires": [
51
+ "`aria-label` or `aria-labelledby` on Table — it names the scroll region, and the container takes no props of its own, so this is the only place the name can live",
52
+ "`scope=\"col\"` on header cells",
53
+ "an accessible name on every row-selection Checkbox — those have no visible label by design"
54
+ ],
55
+ "notes": [
56
+ "Prefer a visible <caption> referenced by aria-labelledby when the table's name should also be seen."
57
+ ]
58
+ },
59
+ "antiPatterns": [
60
+ {
61
+ "dont": "<Table> with no aria-label",
62
+ "why": "the scroll region is announced with no name, and a keyboard user reaching it is told nothing about what they have entered"
63
+ },
64
+ {
65
+ "dont": "header cells without scope",
66
+ "why": "screen readers cannot associate data cells with their column"
67
+ },
68
+ {
69
+ "dont": "wrapping Table in your own overflow container",
70
+ "why": "Table already owns the scroll container and makes it focusable; a second one is unreachable by keyboard"
71
+ },
72
+ {
73
+ "dont": "row-selection checkboxes with no aria-label",
74
+ "why": "every row announces an unnamed checkbox"
75
+ }
76
+ ],
77
+ "missingStates": {
78
+ "note": "Table has no empty, loading or error state of its own — those are the caller's, and they are the states most often forgotten. Pattern coverage is planned; see docs/agent-readiness-plan.md phase 4a."
79
+ },
80
+ "stylesheet": "src/styles/table.css",
81
+ "tokens": [
82
+ "--border-default",
83
+ "--border-focus",
84
+ "--border-width-default",
85
+ "--border-width-thick",
86
+ "--font-family-sans",
87
+ "--font-weight-medium",
88
+ "--font-weight-regular",
89
+ "--icon-interactive",
90
+ "--icon-secondary",
91
+ "--icon-size-sm",
92
+ "--icon-tertiary",
93
+ "--ion-duration-base",
94
+ "--ion-ease-out",
95
+ "--radius-md",
96
+ "--spacing-16",
97
+ "--spacing-20",
98
+ "--spacing-6",
99
+ "--spacing-8",
100
+ "--surface-default",
101
+ "--surface-hover",
102
+ "--surface-page",
103
+ "--surface-primary-subtle",
104
+ "--text-default",
105
+ "--text-link",
106
+ "--text-secondary",
107
+ "--type-body-sm",
108
+ "--type-body-sm-line-height"
109
+ ],
110
+ "props": {
111
+ "density": {
112
+ "type": "TableDensity | undefined",
113
+ "required": false,
114
+ "origin": "own",
115
+ "description": "Matches Figma's `Density` variant on `Table Cell` / `Table Row`.",
116
+ "values": [
117
+ "compact",
118
+ "default",
119
+ "relaxed"
120
+ ]
121
+ },
122
+ "isStriped": {
123
+ "type": "boolean | undefined",
124
+ "required": false,
125
+ "origin": "own",
126
+ "description": "Alternating row background, read from the row's position, not a prop\nrepeated on every `TableRow`."
127
+ }
128
+ },
129
+ "propCounts": {
130
+ "own": 2,
131
+ "aria": 0,
132
+ "dom": 287,
133
+ "other": 0
134
+ }
135
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "TableBody",
3
+ "source": "src/components/Table.tsx",
4
+ "propsType": null,
5
+ "import": "import { TableBody } from 'ionbase-ui';",
6
+ "stylesheet": "src/styles/table.css",
7
+ "tokens": [
8
+ "--border-default",
9
+ "--border-focus",
10
+ "--border-width-default",
11
+ "--border-width-thick",
12
+ "--font-family-sans",
13
+ "--font-weight-medium",
14
+ "--font-weight-regular",
15
+ "--icon-interactive",
16
+ "--icon-secondary",
17
+ "--icon-size-sm",
18
+ "--icon-tertiary",
19
+ "--ion-duration-base",
20
+ "--ion-ease-out",
21
+ "--radius-md",
22
+ "--spacing-16",
23
+ "--spacing-20",
24
+ "--spacing-6",
25
+ "--spacing-8",
26
+ "--surface-default",
27
+ "--surface-hover",
28
+ "--surface-page",
29
+ "--surface-primary-subtle",
30
+ "--text-default",
31
+ "--text-link",
32
+ "--text-secondary",
33
+ "--type-body-sm",
34
+ "--type-body-sm-line-height"
35
+ ],
36
+ "props": {},
37
+ "propCounts": {
38
+ "own": 0,
39
+ "aria": 0,
40
+ "dom": 0,
41
+ "other": 0
42
+ }
43
+ }