ionbase-ui 0.25.0 → 0.27.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,6 +1,6 @@
1
1
  {
2
2
  "package": "ionbase-ui",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "usage": "Pick a component here, then read dist/meta/<Name>.json for its full contract.",
5
5
  "hooks": [
6
6
  "useToast"
@@ -97,11 +97,51 @@
97
97
  "shape": [
98
98
  "circle",
99
99
  "square"
100
+ ],
101
+ "topIndicator": [
102
+ "neutral",
103
+ "primary",
104
+ "success",
105
+ "warning",
106
+ "error",
107
+ "information"
108
+ ],
109
+ "bottomIndicator": [
110
+ "neutral",
111
+ "primary",
112
+ "success",
113
+ "warning",
114
+ "error",
115
+ "information"
100
116
  ]
101
117
  },
102
118
  "detail": "dist/meta/Avatar.json",
103
119
  "hasIntent": true
104
120
  },
121
+ "AvatarGradient": {
122
+ "summary": "Avatar's initials-only sibling: a lit, bevelled disc in one of eight colours, for people with no photo.",
123
+ "status": "stable",
124
+ "variants": {
125
+ "size": [
126
+ "sm",
127
+ "md",
128
+ "lg",
129
+ "mini"
130
+ ],
131
+ "color": [
132
+ "slate",
133
+ "blue",
134
+ "violet",
135
+ "pink",
136
+ "orange",
137
+ "green",
138
+ "red",
139
+ "light"
140
+ ]
141
+ },
142
+ "detail": "dist/meta/AvatarGradient.json",
143
+ "hasIntent": true
144
+ },
105
145
  "AvatarGroup": {
106
146
  "summary": "Overlapping Avatars with a `+N` overflow, for showing several people in one row's width.",
107
147
  "status": "stable",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "ionbase-ui",
3
- "version": "0.25.0",
3
+ "version": "0.27.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,226 @@
1
+ /*
2
+ * Avatar Gradient
3
+ *
4
+ * Measured from Figma `Avatar Gradient` (1054:305). Circle only — the set has
5
+ * no Shape axis, because a gradient this directional does not survive a corner.
6
+ *
7
+ * Sizes and the type ramp are Avatar's, including regular weight at Mini:
8
+ *
9
+ * mini 24 type 12/18 regular
10
+ * small 32 type 14/20 medium
11
+ * medium 40 type 16/24 medium
12
+ * large 48 type 20/32 medium
13
+ *
14
+ * EVERY GRADIENT IS A PAIR ALREADY IN THE PALETTE
15
+ *
16
+ * `--color-<hue>-500` to `--color-<hue>-600`, and `light` is gray 100 to 200.
17
+ * Nothing here needed a new token, which is worth stating because the opposite
18
+ * would have been the expensive outcome: eight bespoke gradient tokens that
19
+ * Figma owns and this repo could only copy.
20
+ *
21
+ * The middle stop is the arithmetic midpoint of the two ends, sitting at 60%
22
+ * rather than 50%. That offset is the whole shape of it — a plain two-stop
23
+ * gradient puts the midpoint at 50% and reads flatter. `color-mix` is how the
24
+ * midpoint stays derived from the pair instead of being a third literal, and
25
+ * it is also the only way to write it at all: `color-no-hex` forbids the hex,
26
+ * and there is no token for a value halfway between two tokens.
27
+ */
28
+ .ion-avatar-gradient {
29
+ --ion-avatar-gradient-size: var(--spacing-40);
30
+ --ion-avatar-gradient-font-size: var(--type-body);
31
+ --ion-avatar-gradient-line-height: var(--type-body-line-height);
32
+ --ion-avatar-gradient-font-weight: var(--font-weight-medium);
33
+
34
+ /* Slate, which is also the block default — Figma's default variant. */
35
+ --ion-avatar-gradient-from: var(--color-gray-500);
36
+ --ion-avatar-gradient-to: var(--color-gray-600);
37
+
38
+ /*
39
+ * White, from the primitive ramp rather than `--text-on-color`.
40
+ *
41
+ * NOT AN OVERSIGHT, AND DO NOT "FIX" IT TO THE SEMANTIC TOKEN. The surface
42
+ * under this text is built from primitives, so it does not theme; the dark
43
+ * theme flips `--text-on-color` to black, which would put black initials on
44
+ * an unchanged mid-blue disc. A foreground has to theme exactly as much as
45
+ * the background it sits on, and this one sits on a background that does not.
46
+ */
47
+ --ion-avatar-gradient-text: var(--color-base-white);
48
+
49
+ /*
50
+ * The sheen: a white radial highlight low and centre, over the linear
51
+ * gradient. Identical in proportion at all four sizes — Figma expresses it as
52
+ * a userSpaceOnUse radial scaled per size, and the ratios come out the same
53
+ * to three decimals, so it is written once as percentages.
54
+ */
55
+ --ion-avatar-gradient-sheen: 18%;
56
+ --ion-avatar-gradient-sheen-mid: 3.6%;
57
+
58
+ /*
59
+ * Bevel and lift.
60
+ *
61
+ * A LOCAL, BECAUSE A RAW COLOUR IN A `box-shadow` DECLARATION IS BANNED — and
62
+ * the ban is right. Its reason is that a raw colour is a place dark mode has
63
+ * to be fixed by hand, once per occurrence; naming it here makes this file
64
+ * exactly one such place instead of five, which is the same trade
65
+ * `elevation.css` makes for the shared ramp. These are local Figma effects,
66
+ * not effect STYLES, so they are not in that ramp and cannot be: the exporter
67
+ * only sees named styles, and there is nothing here for it to name.
68
+ *
69
+ * All three offsets are derived from the box rather than written four times:
70
+ * Figma scales the effect with the component, so the arithmetic IS the
71
+ * design. S/24 and S/12 for the inner shade, S/48 for the top highlight, and
72
+ * S/16 and S/6 for the drop.
73
+ */
74
+ --ion-avatar-gradient-elevation:
75
+ inset 0 calc(-1 * var(--ion-avatar-gradient-size) / 24)
76
+ calc(var(--ion-avatar-gradient-size) / 12) 0 rgb(0 0 0 / 12%),
77
+ inset 0 calc(var(--ion-avatar-gradient-size) / 48)
78
+ calc(var(--ion-avatar-gradient-size) / 48 * 1.4) 0 rgb(255 255 255 / 24%),
79
+ 0 calc(var(--ion-avatar-gradient-size) / 16)
80
+ calc(var(--ion-avatar-gradient-size) / 6) 0 rgb(13 15 23 / 12%);
81
+
82
+ display: inline-flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ flex-shrink: 0;
86
+ overflow: hidden;
87
+ width: var(--ion-avatar-gradient-size);
88
+ height: var(--ion-avatar-gradient-size);
89
+ border-radius: var(--radius-full);
90
+ color: var(--ion-avatar-gradient-text);
91
+
92
+ /*
93
+ * The flat colour under the gradient is the `from` end, and that choice is
94
+ * load-bearing twice over. It is the fallback if `background-image` never
95
+ * paints, and it is the lightest part of the disc — so it is also the worst
96
+ * case for white text, which is what makes `verify-contrast` measure the
97
+ * pairing that actually needs measuring rather than a flattering one.
98
+ */
99
+ background-color: var(--ion-avatar-gradient-from);
100
+ background-image:
101
+ radial-gradient(
102
+ 55.556% 64.103% at 50% 70.513%,
103
+ rgb(255 255 255 / var(--ion-avatar-gradient-sheen)) 0%,
104
+ rgb(255 255 255 / var(--ion-avatar-gradient-sheen-mid)) 60%,
105
+ rgb(255 255 255 / 0%) 100%
106
+ ),
107
+ linear-gradient(
108
+ 180deg,
109
+ var(--ion-avatar-gradient-from) 0%,
110
+ color-mix(
111
+ in srgb,
112
+ var(--ion-avatar-gradient-from),
113
+ var(--ion-avatar-gradient-to)
114
+ )
115
+ 60%,
116
+ var(--ion-avatar-gradient-to) 100%
117
+ );
118
+ box-shadow: var(--ion-avatar-gradient-elevation);
119
+ font-family: var(--font-family-sans);
120
+ font-size: var(--ion-avatar-gradient-font-size);
121
+ line-height: var(--ion-avatar-gradient-line-height);
122
+ font-weight: var(--ion-avatar-gradient-font-weight);
123
+ user-select: none;
124
+ }
125
+
126
+ .ion-avatar-gradient--mini {
127
+ --ion-avatar-gradient-size: var(--spacing-24);
128
+ --ion-avatar-gradient-font-size: var(--type-caption);
129
+ --ion-avatar-gradient-line-height: var(--type-caption-line-height);
130
+ --ion-avatar-gradient-font-weight: var(--font-weight-regular);
131
+ }
132
+
133
+ .ion-avatar-gradient--sm {
134
+ --ion-avatar-gradient-size: var(--spacing-32);
135
+ --ion-avatar-gradient-font-size: var(--type-body-sm);
136
+ --ion-avatar-gradient-line-height: var(--type-body-sm-line-height);
137
+ }
138
+
139
+ .ion-avatar-gradient--lg {
140
+ --ion-avatar-gradient-size: var(--spacing-48);
141
+ --ion-avatar-gradient-font-size: var(--type-body-lg);
142
+ --ion-avatar-gradient-line-height: var(--type-body-lg-line-height);
143
+ }
144
+
145
+ /*
146
+ * The eight colours. Each declares its own `color` and `background-color`
147
+ * rather than inheriting them from the block: the contrast gate pairs what one
148
+ * rule declares, and a colour it has to find on a different scope resolves
149
+ * through the cascade to something that never renders together.
150
+ */
151
+ .ion-avatar-gradient--slate,
152
+ .ion-avatar-gradient--blue,
153
+ .ion-avatar-gradient--violet,
154
+ .ion-avatar-gradient--pink,
155
+ .ion-avatar-gradient--orange,
156
+ .ion-avatar-gradient--green,
157
+ .ion-avatar-gradient--red {
158
+ color: var(--ion-avatar-gradient-text);
159
+ background-color: var(--ion-avatar-gradient-from);
160
+ }
161
+
162
+ .ion-avatar-gradient--slate {
163
+ --ion-avatar-gradient-from: var(--color-gray-500);
164
+ --ion-avatar-gradient-to: var(--color-gray-600);
165
+ }
166
+
167
+ .ion-avatar-gradient--blue {
168
+ --ion-avatar-gradient-from: var(--color-blue-500);
169
+ --ion-avatar-gradient-to: var(--color-blue-600);
170
+ }
171
+
172
+ .ion-avatar-gradient--violet {
173
+ --ion-avatar-gradient-from: var(--color-purple-500);
174
+ --ion-avatar-gradient-to: var(--color-purple-600);
175
+ }
176
+
177
+ .ion-avatar-gradient--pink {
178
+ --ion-avatar-gradient-from: var(--color-pink-500);
179
+ --ion-avatar-gradient-to: var(--color-pink-600);
180
+ }
181
+
182
+ .ion-avatar-gradient--orange {
183
+ --ion-avatar-gradient-from: var(--color-orange-500);
184
+ --ion-avatar-gradient-to: var(--color-orange-600);
185
+ }
186
+
187
+ .ion-avatar-gradient--green {
188
+ --ion-avatar-gradient-from: var(--color-green-500);
189
+ --ion-avatar-gradient-to: var(--color-green-600);
190
+ }
191
+
192
+ .ion-avatar-gradient--red {
193
+ --ion-avatar-gradient-from: var(--color-red-500);
194
+ --ion-avatar-gradient-to: var(--color-red-600);
195
+ }
196
+
197
+ /*
198
+ * Light is the odd one and gets dark text, a stronger sheen and a gentler
199
+ * bevel — Figma halves the shade to 5% and doubles the highlight to 50%,
200
+ * because the same values that read as depth on a saturated disc read as dirt
201
+ * on a near-white one.
202
+ *
203
+ * Its text is `--color-gray-900`, not `--text-default`, for exactly the reason
204
+ * the seven colours use `--color-base-white` rather than `--text-on-color`.
205
+ * The first pass used the semantic token here and the contrast gate caught it
206
+ * in the dark theme at 1.05:1 — `--text-default` themes to near-white while
207
+ * `--color-gray-100` underneath it does not, so the initials disappeared. A
208
+ * foreground has to theme exactly as much as the background it sits on.
209
+ */
210
+ .ion-avatar-gradient--light {
211
+ --ion-avatar-gradient-from: var(--color-gray-100);
212
+ --ion-avatar-gradient-to: var(--color-gray-200);
213
+ --ion-avatar-gradient-text: var(--color-gray-900);
214
+ --ion-avatar-gradient-sheen: 32%;
215
+ --ion-avatar-gradient-sheen-mid: 6.4%;
216
+ --ion-avatar-gradient-elevation:
217
+ inset 0 calc(-1 * var(--ion-avatar-gradient-size) / 24)
218
+ calc(var(--ion-avatar-gradient-size) / 12) 0 rgb(0 0 0 / 5%),
219
+ inset 0 calc(var(--ion-avatar-gradient-size) / 48)
220
+ calc(var(--ion-avatar-gradient-size) / 48 * 1.4) 0 rgb(255 255 255 / 50%),
221
+ 0 calc(var(--ion-avatar-gradient-size) / 16)
222
+ calc(var(--ion-avatar-gradient-size) / 6) 0 rgb(13 15 23 / 12%);
223
+
224
+ color: var(--ion-avatar-gradient-text);
225
+ background-color: var(--ion-avatar-gradient-from);
226
+ }
@@ -1,52 +1,116 @@
1
1
  /*
2
2
  * Avatar
3
3
  *
4
- * Measured from Figma `Avatar` (77:22248) and `Avatar Group`.
4
+ * Measured from Figma `Avatar` (74:164) and `Avatar Group` (74:279).
5
5
  *
6
- * mini 24 type 12/18 icon 12 square radius/xs
7
- * small 32 type 14/20 icon 16 square radius/sm
8
- * medium 40 type 16/24 icon 20 square radius/md
9
- * large 48 type 20/32 icon 24 square radius/md
6
+ * mini 24 type 12/18 regular icon 12 square radius/xs
7
+ * small 32 type 14/20 medium icon 16 square radius/sm
8
+ * medium 40 type 16/24 medium icon 20 square radius/md
9
+ * large 48 type 20/32 medium icon 24 square radius/md
10
10
  *
11
11
  * Circle is radius/full at every size; square steps up with the box, which is
12
12
  * concentric-corner arithmetic rather than one shared corner.
13
13
  *
14
- * Surface is surface/muted with a 1px border/strong at every size and type —
15
- * the border is what separates overlapping avatars in a group, so it is not
16
- * decoration and must not be dropped.
14
+ * Two things here were wrong against Figma until the 2026-08-27 re-export, and
15
+ * both were invisible because they are one token apart from something plausible:
16
+ *
17
+ * Large used `type/h5` (20/28). The style Figma applies is Body/Large
18
+ * Emphasis (20/32). Same font-size, four pixels of leading apart.
19
+ *
20
+ * Mini used font-weight/medium. Figma's Mini is the Caption style, which is
21
+ * font-weight/regular. Every other size is Emphasis and stays medium.
22
+ *
23
+ * Surface and border depend on the Type variant, which is the other correction:
24
+ * Character and Icon sit on surface/muted behind border/strong, but Image sits
25
+ * on surface/placeholder behind border/subtle — a photo needs a quieter frame
26
+ * than an empty box does. The border is what separates overlapping avatars in a
27
+ * group, so it is not decoration and must not be dropped from either.
28
+ *
29
+ * THE ROOT DOES NOT CLIP, AND THAT IS LODAD-BEARING
30
+ *
31
+ * `.ion-avatar__media` carries the surface, the border and the `overflow` that
32
+ * crops the photo. The root carries only geometry. They cannot be the same box:
33
+ * an indicator sits in a corner that is OUTSIDE a circle's clip path, so a root
34
+ * that clipped the image would slice the indicator in half. Figma draws it the
35
+ * same way — an unclipped frame around a clipped `Media` child, with the ring
36
+ * and the indicators as siblings of it.
17
37
  */
18
38
  .ion-avatar {
19
39
  --ion-avatar-size: var(--spacing-40);
20
40
  --ion-avatar-radius: var(--radius-md);
21
41
  --ion-avatar-font-size: var(--type-body);
22
42
  --ion-avatar-line-height: var(--type-body-line-height);
43
+ --ion-avatar-font-weight: var(--font-weight-medium);
23
44
  --ion-avatar-icon: var(--icon-size-md);
45
+ --ion-avatar-surface: var(--surface-muted);
46
+ --ion-avatar-border: var(--border-strong);
47
+
48
+ /* Ring and indicator geometry, indexed by size the way everything else is. */
49
+ --ion-avatar-ring-width: var(--border-width-thick);
50
+ --ion-avatar-indicator-size: var(--spacing-14);
51
+
52
+ /*
53
+ * 1.5px, and it cannot be a token: the border ladder is 1 / 2 / 4 and Figma
54
+ * puts a half-step here so the 14px indicator keeps the same visual gap the
55
+ * 16px one gets from 2px. Written once, as a local, rather than inline —
56
+ * a raw length in a `border-width` declaration is a theming bug everywhere
57
+ * else in this system and the lint rule is right to say so.
58
+ */
59
+ --ion-avatar-indicator-ring: 1.5px;
60
+
61
+ /*
62
+ * The glyph inside the top indicator: 11 / 10 / 8 / 6 against a 16 / 14 / 12
63
+ * / 8 dot. Not a ratio and not a rung on the icon ladder — Figma tuned these
64
+ * four by eye, and the smallest is below anything `--icon-size-*` carries.
65
+ */
66
+ --ion-avatar-indicator-icon: 10px;
24
67
 
68
+ position: relative;
25
69
  display: inline-flex;
26
- align-items: center;
27
- justify-content: center;
28
70
  flex-shrink: 0;
29
- overflow: hidden;
30
71
  width: var(--ion-avatar-size);
31
72
  height: var(--ion-avatar-size);
32
- background-color: var(--surface-muted);
33
- border-style: solid;
34
- border-width: var(--border-width-default);
35
- border-color: var(--border-strong);
36
73
  border-radius: var(--ion-avatar-radius);
37
74
  color: var(--text-tertiary);
38
75
  font-family: var(--font-family-sans);
39
76
  font-size: var(--ion-avatar-font-size);
40
77
  line-height: var(--ion-avatar-line-height);
41
- font-weight: var(--font-weight-medium);
78
+ font-weight: var(--ion-avatar-font-weight);
42
79
  user-select: none;
43
80
  }
44
81
 
82
+ .ion-avatar__media {
83
+ position: absolute;
84
+ inset: 0;
85
+ display: flex;
86
+ align-items: center;
87
+ justify-content: center;
88
+ overflow: hidden;
89
+ background-color: var(--ion-avatar-surface);
90
+ border-style: solid;
91
+ border-width: var(--border-width-default);
92
+ border-color: var(--ion-avatar-border);
93
+ border-radius: inherit;
94
+ }
95
+
96
+ /* Figma's `Type=Image`: a photo gets the placeholder surface and the quiet
97
+ * border. Applied from `showImage`, not from `src`, so a 404 that falls back
98
+ * to initials also falls back to the Character variant's frame. */
99
+ .ion-avatar--image {
100
+ --ion-avatar-surface: var(--surface-placeholder);
101
+ --ion-avatar-border: var(--border-subtle);
102
+ }
103
+
45
104
  .ion-avatar--mini {
46
105
  --ion-avatar-size: var(--spacing-24);
47
106
  --ion-avatar-radius: var(--radius-xs);
48
107
  --ion-avatar-font-size: var(--type-caption);
49
108
  --ion-avatar-line-height: var(--type-caption-line-height);
109
+ --ion-avatar-font-weight: var(--font-weight-regular);
110
+ --ion-avatar-ring-width: var(--border-width-default);
111
+ --ion-avatar-indicator-size: var(--spacing-8);
112
+ --ion-avatar-indicator-ring: var(--border-width-default);
113
+ --ion-avatar-indicator-icon: 6px;
50
114
 
51
115
  /*
52
116
  * `2xs`, not `xs` — this is 12 and must stay 12.
@@ -64,21 +128,28 @@
64
128
  --ion-avatar-font-size: var(--type-body-sm);
65
129
  --ion-avatar-line-height: var(--type-body-sm-line-height);
66
130
  --ion-avatar-icon: var(--icon-size-sm);
131
+ --ion-avatar-ring-width: var(--border-width-default);
132
+ --ion-avatar-indicator-size: var(--spacing-12);
133
+ --ion-avatar-indicator-ring: var(--border-width-default);
134
+ --ion-avatar-indicator-icon: 8px;
67
135
  }
68
136
 
69
137
  /* Large shares Medium's corner — the square ladder stops climbing at radius/md. */
70
138
  .ion-avatar--lg {
71
139
  --ion-avatar-size: var(--spacing-48);
72
- --ion-avatar-font-size: var(--type-h5);
73
- --ion-avatar-line-height: var(--type-h5-line-height);
140
+ --ion-avatar-font-size: var(--type-body-lg);
141
+ --ion-avatar-line-height: var(--type-body-lg-line-height);
74
142
  --ion-avatar-icon: var(--icon-size-lg);
143
+ --ion-avatar-indicator-size: var(--spacing-16);
144
+ --ion-avatar-indicator-ring: var(--border-width-thick);
145
+ --ion-avatar-indicator-icon: 11px;
75
146
  }
76
147
 
77
148
  .ion-avatar--circle {
78
149
  --ion-avatar-radius: var(--radius-full);
79
150
  }
80
151
 
81
- /* The image fills the box and is cropped by `overflow: hidden` on the parent,
152
+ /* The image fills the box and is cropped by `overflow: hidden` on the media,
82
153
  * so it inherits the corner without repeating it. */
83
154
  .ion-avatar__image {
84
155
  width: 100%;
@@ -99,6 +170,101 @@
99
170
  height: var(--ion-avatar-icon);
100
171
  }
101
172
 
173
+ /*
174
+ * Ring — Figma's `Show Ring`.
175
+ *
176
+ * Drawn INSIDE the avatar's own edge, over the media, at the same size as the
177
+ * box. Not an `outline` and not a `box-shadow` spread: both of those sit
178
+ * outside the element and would change the avatar's footprint, which breaks the
179
+ * group's overlap arithmetic and any layout that measured the avatar. Figma
180
+ * draws a same-size overlay, so this does too.
181
+ */
182
+ .ion-avatar__ring {
183
+ position: absolute;
184
+ inset: 0;
185
+ pointer-events: none;
186
+ border-style: solid;
187
+ border-width: var(--ion-avatar-ring-width);
188
+ border-color: var(--surface-primary);
189
+ border-radius: inherit;
190
+ }
191
+
192
+ /*
193
+ * Indicators — Figma's `Show Top Indicator` / `Show Bottom Indicator`, each an
194
+ * instance of the `Status Indicator` component (1008:1764).
195
+ *
196
+ * Both sit flush in a corner: Figma's `left` is size minus indicator at every
197
+ * size and shape, which is `right: 0`. The border is `ring/offset`, so the dot
198
+ * reads as separate from the photo underneath it rather than as part of it —
199
+ * that is why it is a border and not a gap.
200
+ */
201
+ .ion-avatar__indicator {
202
+ position: absolute;
203
+ right: 0;
204
+ display: inline-flex;
205
+ align-items: center;
206
+ justify-content: center;
207
+ width: var(--ion-avatar-indicator-size);
208
+ height: var(--ion-avatar-indicator-size);
209
+ border-style: solid;
210
+ border-width: var(--ion-avatar-indicator-ring);
211
+ border-color: var(--ring-offset);
212
+ border-radius: var(--radius-full);
213
+ }
214
+
215
+ .ion-avatar__indicator--top {
216
+ top: 0;
217
+ }
218
+
219
+ .ion-avatar__indicator--bottom {
220
+ bottom: 0;
221
+ }
222
+
223
+ .ion-avatar__indicator svg {
224
+ width: var(--ion-avatar-indicator-icon);
225
+ height: var(--ion-avatar-indicator-icon);
226
+ }
227
+
228
+ /*
229
+ * The six intents, spelled the same way Badge and Alert spell them. Colour and
230
+ * surface are declared together per intent rather than hoisted to the block:
231
+ * the contrast gate pairs what one rule declares, and a foreground it has to
232
+ * find on a different element resolves to the block's `--text-tertiary` and
233
+ * measures a pairing that never renders.
234
+ */
235
+ .ion-avatar__indicator--neutral,
236
+ .ion-avatar__indicator--primary,
237
+ .ion-avatar__indicator--success,
238
+ .ion-avatar__indicator--warning,
239
+ .ion-avatar__indicator--error,
240
+ .ion-avatar__indicator--information {
241
+ color: var(--text-on-color);
242
+ }
243
+
244
+ .ion-avatar__indicator--neutral {
245
+ background-color: var(--surface-inverse);
246
+ }
247
+
248
+ .ion-avatar__indicator--primary {
249
+ background-color: var(--surface-primary);
250
+ }
251
+
252
+ .ion-avatar__indicator--success {
253
+ background-color: var(--surface-success);
254
+ }
255
+
256
+ .ion-avatar__indicator--warning {
257
+ background-color: var(--surface-warning);
258
+ }
259
+
260
+ .ion-avatar__indicator--error {
261
+ background-color: var(--surface-error);
262
+ }
263
+
264
+ .ion-avatar__indicator--information {
265
+ background-color: var(--surface-information);
266
+ }
267
+
102
268
  /*
103
269
  * Avatar Group
104
270
  *
@@ -39,6 +39,7 @@
39
39
  @import url('./toggle.css');
40
40
  @import url('./menu.css');
41
41
  @import url('./avatar.css');
42
+ @import url('./avatar-gradient.css');
42
43
  @import url('./header.css');
43
44
  @import url('./logo.css');
44
45
  @import url('./table.css');
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.25.0. 44 components.
5
+ Version 0.27.0. 45 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
- `AgentActivity`, `AgentActivityStep`, `AgentStop`, `Alert`, `ApprovalGate`, `Avatar`, `AvatarGroup`, `Badge`, `Button`, `Checkbox`, `Citation`, `CitationList`, `CitationListItem`, `ConfidenceIndicator`, `Divider`, `FullCard`, `Header`, `Icon`, `Input`, `Link`, `Logo`, `LogoMark`, `Menu`, `MenuItem`, `Modal`, `NavItem`, `PhoneInput`, `Popover`, `Radio`, `RadioGroup`, `ScrollProgress`, `Select`, `StreamingText`, `TabItem`, `Table`, `TableBody`, `TableCell`, `TableHead`, `TableRow`, `Tabs`, `Toast`, `ToastProvider`, `Toggle`, `Tooltip`
16
+ `AgentActivity`, `AgentActivityStep`, `AgentStop`, `Alert`, `ApprovalGate`, `Avatar`, `AvatarGradient`, `AvatarGroup`, `Badge`, `Button`, `Checkbox`, `Citation`, `CitationList`, `CitationListItem`, `ConfidenceIndicator`, `Divider`, `FullCard`, `Header`, `Icon`, `Input`, `Link`, `Logo`, `LogoMark`, `Menu`, `MenuItem`, `Modal`, `NavItem`, `PhoneInput`, `Popover`, `Radio`, `RadioGroup`, `ScrollProgress`, `Select`, `StreamingText`, `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.25.0",
3
+ "version": "0.27.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",