ionbase-ui 0.22.0 → 0.24.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.
@@ -1,11 +1,24 @@
1
1
  {
2
2
  "package": "ionbase-ui",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "usage": "Pick a component here, then read dist/meta/<Name>.json for its full contract.",
5
5
  "hooks": [
6
6
  "useToast"
7
7
  ],
8
8
  "components": {
9
+ "AgentStop": {
10
+ "summary": "The always-visible way to end a running agent. Not optional, and not behind a menu.",
11
+ "status": "stable",
12
+ "variants": {
13
+ "size": [
14
+ "sm",
15
+ "md",
16
+ "lg"
17
+ ]
18
+ },
19
+ "detail": "dist/meta/AgentStop.json",
20
+ "hasIntent": true
21
+ },
9
22
  "Alert": {
10
23
  "summary": "A persistent, in-page status message. Intent picks the ARIA role for you.",
11
24
  "status": "stable",
@@ -30,15 +43,34 @@
30
43
  "detail": "dist/meta/Alert.json",
31
44
  "hasIntent": true
32
45
  },
46
+ "ApprovalGate": {
47
+ "summary": "A proposed action held until a person approves, rejects or amends it. It renders the decision; it does not enforce it.",
48
+ "status": "stable",
49
+ "variants": {
50
+ "risk": [
51
+ "low",
52
+ "medium",
53
+ "high"
54
+ ],
55
+ "status": [
56
+ "pending",
57
+ "approved",
58
+ "rejected",
59
+ "expired"
60
+ ]
61
+ },
62
+ "detail": "dist/meta/ApprovalGate.json",
63
+ "hasIntent": true
64
+ },
33
65
  "Avatar": {
34
66
  "summary": "A person or entity marker. Content is chosen by precedence — image, then initials, then icon — not by a type prop.",
35
67
  "status": "stable",
36
68
  "variants": {
37
69
  "size": [
38
- "mini",
39
70
  "sm",
40
71
  "md",
41
- "lg"
72
+ "lg",
73
+ "mini"
42
74
  ],
43
75
  "shape": [
44
76
  "circle",
@@ -53,10 +85,10 @@
53
85
  "status": "stable",
54
86
  "variants": {
55
87
  "size": [
56
- "mini",
57
88
  "sm",
58
89
  "md",
59
- "lg"
90
+ "lg",
91
+ "mini"
60
92
  ],
61
93
  "shape": [
62
94
  "circle",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "ionbase-ui",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "usage": "A pattern is a documented composition of components, not a component. Read one here, then read the contracts of what it composes.",
5
5
  "patterns": {
6
6
  "DataTable": {
@@ -0,0 +1,109 @@
1
+ /*
2
+ * AgentStop
3
+ *
4
+ * NOT DRAWN IN FIGMA. This component has no Figma counterpart yet, so the
5
+ * measurements are borrowed rather than invented: the size ramp, radius, focus
6
+ * ring and icon sizing are Button's, read from the same tokens. When Figma
7
+ * draws it, this file should need values changed, not structure.
8
+ *
9
+ * DELIBERATELY NOT `surface/error`. Stopping a run is normal and expected, and
10
+ * you can start it again. Colouring it like a delete teaches hesitation about
11
+ * the one control that must never be hesitated over. It reads as a firm
12
+ * neutral: strong border, plain surface, error colour only on hover, where it
13
+ * confirms rather than warns.
14
+ */
15
+ .ion-agent-stop {
16
+ display: inline-flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ gap: var(--spacing-8);
20
+ min-height: var(--spacing-40);
21
+ padding: var(--spacing-8) var(--spacing-16);
22
+ border: var(--border-width-default) solid var(--border-strong);
23
+ border-radius: var(--radius-md);
24
+ background-color: var(--surface-default);
25
+ color: var(--text-default);
26
+ font-family: var(--font-family-sans);
27
+ font-size: var(--type-body);
28
+ line-height: var(--type-body-line-height);
29
+ font-weight: var(--font-weight-medium);
30
+ cursor: pointer;
31
+ transition:
32
+ background-color var(--ion-duration-base) var(--ion-ease-out),
33
+ border-color var(--ion-duration-base) var(--ion-ease-out),
34
+ color var(--ion-duration-base) var(--ion-ease-out);
35
+ }
36
+
37
+ .ion-agent-stop:focus-visible {
38
+ outline: var(--border-width-thick) solid var(--border-focus);
39
+ outline-offset: 2px;
40
+ }
41
+
42
+ /*
43
+ * Disabled ONLY while the stop is in flight, never as a resting state. A
44
+ * greyed-out stop button on a running agent is the failure this component
45
+ * exists to prevent, so the styling deliberately does not read as
46
+ * "unavailable" — it reads as "already asked".
47
+ *
48
+ * Declared before :hover so specificity reads non-decreasing; the :not(:disabled)
49
+ * on the hover rules is what keeps them from fighting.
50
+ */
51
+ .ion-agent-stop:disabled {
52
+ cursor: default;
53
+ background-color: var(--surface-muted);
54
+ border-color: var(--border-default);
55
+ color: var(--text-secondary);
56
+ }
57
+
58
+ .ion-agent-stop:hover:not(:disabled) {
59
+ background-color: var(--surface-error-subtle);
60
+ border-color: var(--border-error);
61
+ color: var(--text-error);
62
+ }
63
+
64
+ .ion-agent-stop:active:not(:disabled) {
65
+ background-color: var(--surface-error-subtle-hover);
66
+ }
67
+
68
+ .ion-agent-stop__glyph {
69
+ display: inline-flex;
70
+ flex: none;
71
+ width: var(--icon-size-sm);
72
+ height: var(--icon-size-sm);
73
+ }
74
+
75
+ .ion-agent-stop__glyph svg {
76
+ width: 100%;
77
+ height: 100%;
78
+ }
79
+
80
+ /* The square shrinks while stopping — the one motion cue that the press
81
+ * landed, on a control whose text also changed. */
82
+ .ion-agent-stop[data-stopping] .ion-agent-stop__glyph svg {
83
+ transform: scale(0.7);
84
+ transition: transform var(--ion-duration-base) var(--ion-ease-out);
85
+ }
86
+
87
+ .ion-agent-stop--sm {
88
+ min-height: var(--spacing-32);
89
+ padding: var(--spacing-4) var(--spacing-12);
90
+ font-size: var(--type-body-sm);
91
+ line-height: var(--type-body-sm-line-height);
92
+ }
93
+
94
+ .ion-agent-stop--sm .ion-agent-stop__glyph {
95
+ width: var(--icon-size-xs);
96
+ height: var(--icon-size-xs);
97
+ }
98
+
99
+ .ion-agent-stop--lg {
100
+ min-height: var(--spacing-48);
101
+ padding: var(--spacing-12) var(--spacing-20);
102
+ font-size: var(--type-body-lg);
103
+ line-height: var(--type-body-lg-line-height);
104
+ }
105
+
106
+ .ion-agent-stop--lg .ion-agent-stop__glyph {
107
+ width: var(--icon-size-md);
108
+ height: var(--icon-size-md);
109
+ }
@@ -0,0 +1,181 @@
1
+ /*
2
+ * ApprovalGate
3
+ *
4
+ * NOT DRAWN IN FIGMA. Structure is Alert's — icon rail, body, one variable per
5
+ * colour slot — so that when Figma draws it, this file needs values changed
6
+ * rather than rewriting. The action row borrows Button's ramp.
7
+ *
8
+ * RISK CHANGES EMPHASIS, NOT MEANING
9
+ *
10
+ * Three risk tiers reassign four variables, exactly the way Alert's intents do.
11
+ * `low` sits on the neutral surface, `medium` on information, `high` on
12
+ * warning — NOT on error. Error is for something that has gone wrong; a
13
+ * high-risk approval is working correctly, and it is asking. Reserving red for
14
+ * failure is what keeps red meaningful when a failure actually happens.
15
+ */
16
+ .ion-approval-gate {
17
+ --ion-approval-surface: var(--surface-information-subtle);
18
+ --ion-approval-border: var(--border-information);
19
+ --ion-approval-icon: var(--icon-information);
20
+ --ion-approval-accent: var(--text-information);
21
+
22
+ position: relative;
23
+ display: flex;
24
+ align-items: flex-start;
25
+ gap: var(--spacing-12);
26
+ padding: var(--spacing-16);
27
+ background-color: var(--ion-approval-surface);
28
+
29
+ /*
30
+ * A thicker leading edge, not a heavier box. A decision waiting on a person
31
+ * has to be findable at a glance down a long transcript, and a full heavy
32
+ * border would compete with every Alert on the page.
33
+ */
34
+ border: var(--border-width-default) solid var(--ion-approval-border);
35
+ border-inline-start-width: var(--border-width-thicker);
36
+ border-radius: var(--radius-md);
37
+ color: var(--text-default);
38
+ font-family: var(--font-family-sans);
39
+ font-size: var(--type-body);
40
+ line-height: var(--type-body-line-height);
41
+ }
42
+
43
+ .ion-approval-gate--low {
44
+ --ion-approval-surface: var(--surface-muted);
45
+ --ion-approval-border: var(--border-default);
46
+ --ion-approval-icon: var(--icon-secondary);
47
+ --ion-approval-accent: var(--text-secondary);
48
+ }
49
+
50
+ .ion-approval-gate--high {
51
+ --ion-approval-surface: var(--surface-warning-subtle);
52
+ --ion-approval-border: var(--border-warning-strong);
53
+ --ion-approval-icon: var(--icon-warning);
54
+ --ion-approval-accent: var(--text-warning);
55
+ }
56
+
57
+ /* Resolved: the decision is a record now, not a question. Drop the tint and
58
+ * keep the rule, so the row stays scannable without still looking pending. */
59
+ .ion-approval-gate--approved,
60
+ .ion-approval-gate--rejected,
61
+ .ion-approval-gate--expired {
62
+ --ion-approval-surface: var(--surface-default);
63
+ --ion-approval-border: var(--border-default);
64
+ --ion-approval-icon: var(--icon-tertiary);
65
+ }
66
+
67
+ .ion-approval-gate--approved {
68
+ --ion-approval-border: var(--border-success);
69
+ --ion-approval-icon: var(--icon-success);
70
+ --ion-approval-accent: var(--text-success);
71
+ }
72
+
73
+ .ion-approval-gate--rejected {
74
+ --ion-approval-accent: var(--text-secondary);
75
+ }
76
+
77
+ .ion-approval-gate__icon {
78
+ display: inline-flex;
79
+ flex: none;
80
+ width: var(--icon-size-md);
81
+ height: var(--icon-size-md);
82
+ color: var(--ion-approval-icon);
83
+ }
84
+
85
+ .ion-approval-gate__icon svg {
86
+ width: 100%;
87
+ height: 100%;
88
+ }
89
+
90
+ .ion-approval-gate__body {
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: var(--spacing-8);
94
+ min-width: 0;
95
+ flex: 1;
96
+ }
97
+
98
+ .ion-approval-gate__title {
99
+ margin: 0;
100
+ font-weight: var(--font-weight-semibold);
101
+ color: var(--text-default);
102
+ }
103
+
104
+ /* Focusable only programmatically, so it takes a ring but no tab stop. */
105
+ .ion-approval-gate__title:focus-visible {
106
+ outline: var(--border-width-thick) solid var(--border-focus);
107
+ outline-offset: 2px;
108
+ border-radius: var(--radius-xs);
109
+ }
110
+
111
+ .ion-approval-gate__detail {
112
+ margin: 0;
113
+ color: var(--text-secondary);
114
+ font-size: var(--type-body-sm);
115
+ line-height: var(--type-body-sm-line-height);
116
+ }
117
+
118
+ .ion-approval-gate__actions {
119
+ display: flex;
120
+ flex-wrap: wrap;
121
+ gap: var(--spacing-8);
122
+ margin-top: var(--spacing-4);
123
+ }
124
+
125
+ .ion-approval-gate__action {
126
+ display: inline-flex;
127
+ align-items: center;
128
+ justify-content: center;
129
+ min-height: var(--spacing-32);
130
+ padding: var(--spacing-4) var(--spacing-12);
131
+ border: var(--border-width-default) solid var(--border-strong);
132
+ border-radius: var(--radius-sm);
133
+ background-color: var(--surface-default);
134
+ color: var(--text-default);
135
+ font-family: var(--font-family-sans);
136
+ font-size: var(--type-body-sm);
137
+ line-height: var(--type-body-sm-line-height);
138
+ font-weight: var(--font-weight-medium);
139
+ cursor: pointer;
140
+ transition:
141
+ background-color var(--ion-duration-base) var(--ion-ease-out),
142
+ border-color var(--ion-duration-base) var(--ion-ease-out);
143
+ }
144
+
145
+ .ion-approval-gate__action:focus-visible {
146
+ outline: var(--border-width-thick) solid var(--border-focus);
147
+ outline-offset: 2px;
148
+ }
149
+
150
+ /* Before :hover so specificity reads non-decreasing. */
151
+ .ion-approval-gate__action:disabled {
152
+ cursor: default;
153
+ background-color: var(--surface-disabled);
154
+ border-color: var(--border-disabled);
155
+ color: var(--text-disabled);
156
+ }
157
+
158
+ .ion-approval-gate__action:hover:not(:disabled) {
159
+ background-color: var(--surface-hover);
160
+ }
161
+
162
+ /*
163
+ * Approve is the only filled button, and it is filled in brand rather than
164
+ * success. Success would state an outcome before there is one.
165
+ */
166
+ .ion-approval-gate__action--approve {
167
+ background-color: var(--surface-primary);
168
+ border-color: var(--surface-primary);
169
+ color: var(--text-on-color);
170
+ }
171
+
172
+ .ion-approval-gate__action--approve:hover:not(:disabled) {
173
+ background-color: var(--surface-primary-hover);
174
+ border-color: var(--surface-primary-hover);
175
+ }
176
+
177
+ .ion-approval-gate__resolution {
178
+ margin: 0;
179
+ font-weight: var(--font-weight-medium);
180
+ color: var(--ion-approval-accent);
181
+ }
@@ -52,6 +52,36 @@
52
52
  @import url('./scroll-progress.css');
53
53
  @import url('./full-card.css');
54
54
 
55
+ /*
56
+ * The agentic tier. No Figma counterpart yet, so these borrow their
57
+ * measurements from Button and Alert rather than inventing any — see the
58
+ * header of each file.
59
+ */
60
+ @import url('./agent-stop.css');
61
+ @import url('./approval-gate.css');
62
+
63
+ /*
64
+ * Visually hidden, but not hidden from assistive tech.
65
+ *
66
+ * Written once here rather than per component: two components already needed
67
+ * it for their live regions, and a second copy of a rule this fiddly is how the
68
+ * two quietly diverge. `display: none` and `visibility: hidden` both remove the
69
+ * node from the accessibility tree, so an announcement placed in one would
70
+ * never be made — the clip-path is what keeps it readable while occupying no
71
+ * space. No `margin: -1px`: that belongs to the older `clip: rect()` variant
72
+ * and is not needed here, which is convenient, because it is also not a token.
73
+ */
74
+ .ion-visually-hidden {
75
+ position: absolute;
76
+ width: 1px;
77
+ height: 1px;
78
+ padding: 0;
79
+ overflow: hidden;
80
+ clip-path: inset(50%);
81
+ white-space: nowrap;
82
+ border: 0;
83
+ }
84
+
55
85
  /*
56
86
  * Every IonBase element is border-box.
57
87
  *
package/llms.txt CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > IonBase is a React design system for enterprise SaaS, built to be consumed by an agent rather than read by a developer. Every component ships a machine-readable contract carrying the judgement a type signature cannot: when to use it, what to use instead, the anti-patterns, and what it guarantees for accessibility versus what it requires of you.
4
4
 
5
- Version 0.22.0. 35 components.
5
+ Version 0.24.0. 37 components.
6
6
 
7
7
  ## Read these, in this order
8
8
 
@@ -13,7 +13,7 @@ Both are importable: `ionbase-ui/meta/index` and `ionbase-ui/meta/<Name>.json`.
13
13
 
14
14
  ## Components
15
15
 
16
- `Alert`, `Avatar`, `AvatarGroup`, `Badge`, `Button`, `Checkbox`, `Divider`, `FullCard`, `Header`, `Icon`, `Input`, `Link`, `Logo`, `LogoMark`, `Menu`, `MenuItem`, `Modal`, `NavItem`, `PhoneInput`, `Popover`, `Radio`, `RadioGroup`, `ScrollProgress`, `Select`, `TabItem`, `Table`, `TableBody`, `TableCell`, `TableHead`, `TableRow`, `Tabs`, `Toast`, `ToastProvider`, `Toggle`, `Tooltip`
16
+ `AgentStop`, `Alert`, `ApprovalGate`, `Avatar`, `AvatarGroup`, `Badge`, `Button`, `Checkbox`, `Divider`, `FullCard`, `Header`, `Icon`, `Input`, `Link`, `Logo`, `LogoMark`, `Menu`, `MenuItem`, `Modal`, `NavItem`, `PhoneInput`, `Popover`, `Radio`, `RadioGroup`, `ScrollProgress`, `Select`, `TabItem`, `Table`, `TableBody`, `TableCell`, `TableHead`, `TableRow`, `Tabs`, `Toast`, `ToastProvider`, `Toggle`, `Tooltip`
17
17
 
18
18
  ## Patterns
19
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ionbase-ui",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "IonBase Design System — accessible React components, styles and design tokens in one package",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "scripts": {
64
64
  "dev": "tsc --watch",
65
- "build": "node scripts/sync-tokens.mjs && tsc --build --force && node scripts/copy-css.mjs && node scripts/verify-client-boundaries.mjs && node scripts/verify-contrast.mjs && node scripts/build-meta.mjs && node scripts/verify-meta.mjs && node scripts/build-patterns.mjs && node scripts/verify-figma-map.mjs && node scripts/build-llms.mjs",
65
+ "build": "node scripts/sync-tokens.mjs && tsc --build --force && node scripts/copy-css.mjs && node scripts/verify-client-boundaries.mjs && node scripts/verify-contrast.mjs && node scripts/build-meta.mjs && node scripts/verify-meta.mjs && node scripts/build-patterns.mjs && node scripts/verify-figma-map.mjs && node scripts/build-figma-descriptions.mjs && node scripts/build-llms.mjs",
66
66
  "typecheck": "tsc --noEmit",
67
67
  "lint": "eslint . && stylelint \"src/styles/*.css\"",
68
68
  "format": "prettier --check .",
@@ -73,7 +73,8 @@
73
73
  "contrast:list": "node scripts/verify-contrast.mjs --list",
74
74
  "llms": "node scripts/build-llms.mjs",
75
75
  "patterns": "node scripts/build-patterns.mjs",
76
- "figma:map": "node scripts/verify-figma-map.mjs"
76
+ "figma:map": "node scripts/verify-figma-map.mjs",
77
+ "figma:descriptions": "node scripts/build-figma-descriptions.mjs"
77
78
  },
78
79
  "peerDependencies": {
79
80
  "react": "^18.0.0 || ^19.0.0",