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.
- package/dist/components/Avatar.d.ts +33 -0
- package/dist/components/Avatar.d.ts.map +1 -1
- package/dist/components/Avatar.js +43 -4
- package/dist/components/Avatar.js.map +1 -1
- package/dist/components/AvatarGradient.d.ts +41 -0
- package/dist/components/AvatarGradient.d.ts.map +1 -0
- package/dist/components/AvatarGradient.js +32 -0
- package/dist/components/AvatarGradient.js.map +1 -0
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/figma-descriptions.json +34 -29
- package/dist/figma-map.json +115 -3
- package/dist/meta/Avatar.json +137 -7
- package/dist/meta/AvatarGradient.json +239 -0
- package/dist/meta/AvatarGroup.json +26 -3
- package/dist/meta/components.json +403 -11
- package/dist/meta/contrast.json +542 -162
- package/dist/meta/index.json +41 -1
- package/dist/meta/patterns/index.json +1 -1
- package/dist/styles/avatar-gradient.css +226 -0
- package/dist/styles/avatar.css +185 -19
- package/dist/styles/index.css +1 -0
- package/llms.txt +2 -2
- package/package.json +1 -1
package/dist/meta/index.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "ionbase-ui",
|
|
3
|
-
"version": "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",
|
|
@@ -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
|
+
}
|
package/dist/styles/avatar.css
CHANGED
|
@@ -1,52 +1,116 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Avatar
|
|
3
3
|
*
|
|
4
|
-
* Measured from Figma `Avatar` (
|
|
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
|
|
8
|
-
* medium 40 type 16/24
|
|
9
|
-
* large 48 type 20/32
|
|
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
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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
|
|
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-
|
|
73
|
-
--ion-avatar-line-height: var(--type-
|
|
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
|
|
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
|
*
|
package/dist/styles/index.css
CHANGED
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.
|
|
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
|
|