ionbase-ui 0.26.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 +96 -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/figma-map.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "ionbase-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"figmaFile": "gaLbGd0QNb1fUl6BjSpfBA",
|
|
5
|
-
"figmaExported": "2026-08-
|
|
5
|
+
"figmaExported": "2026-08-27",
|
|
6
6
|
"usage": "Given a Figma node id or component name, look it up here for the React component, its import, and how each Figma property maps onto a real prop. Every mapping is verified against both the Figma export and the TypeScript API on each build.",
|
|
7
7
|
"generated": "by scripts/verify-figma-map.mjs — do not edit",
|
|
8
8
|
"byNodeId": {
|
|
@@ -729,6 +729,17 @@
|
|
|
729
729
|
},
|
|
730
730
|
"Icon#74:50": {
|
|
731
731
|
"prop": "icon"
|
|
732
|
+
},
|
|
733
|
+
"Show Ring#1009:0": {
|
|
734
|
+
"prop": "ring"
|
|
735
|
+
},
|
|
736
|
+
"Show Top Indicator#1009:25": {
|
|
737
|
+
"prop": "topIndicator",
|
|
738
|
+
"note": "Figma needs a boolean because the intent lives on the nested Status Indicator instance; code takes the intent itself, so passing one is what shows the mark. The drawn instance is Intent=Primary with its check glyph, which is what `topIndicator=\"primary\"` renders."
|
|
739
|
+
},
|
|
740
|
+
"Show Bottom Indicator#1009:50": {
|
|
741
|
+
"prop": "bottomIndicator",
|
|
742
|
+
"note": "As the top indicator — the intent comes from the nested Status Indicator, which a boolean cannot carry. The drawn instance is Intent=Success with no glyph."
|
|
732
743
|
}
|
|
733
744
|
}
|
|
734
745
|
},
|
|
@@ -770,6 +781,41 @@
|
|
|
770
781
|
}
|
|
771
782
|
}
|
|
772
783
|
},
|
|
784
|
+
"1054:305": {
|
|
785
|
+
"figmaComponent": "Avatar Gradient",
|
|
786
|
+
"figmaNodeId": "1054:305",
|
|
787
|
+
"figmaPage": "Avatar",
|
|
788
|
+
"component": "AvatarGradient",
|
|
789
|
+
"import": "import { AvatarGradient } from 'ionbase-ui';",
|
|
790
|
+
"docs": "components/avatar-gradient/index.html.md",
|
|
791
|
+
"props": {
|
|
792
|
+
"Size": {
|
|
793
|
+
"prop": "size",
|
|
794
|
+
"values": {
|
|
795
|
+
"Mini": "mini",
|
|
796
|
+
"Small": "sm",
|
|
797
|
+
"Medium": "md",
|
|
798
|
+
"Large": "lg"
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
"Color": {
|
|
802
|
+
"prop": "color",
|
|
803
|
+
"values": {
|
|
804
|
+
"Slate": "slate",
|
|
805
|
+
"Blue": "blue",
|
|
806
|
+
"Violet": "violet",
|
|
807
|
+
"Pink": "pink",
|
|
808
|
+
"Orange": "orange",
|
|
809
|
+
"Green": "green",
|
|
810
|
+
"Red": "red",
|
|
811
|
+
"Light": "light"
|
|
812
|
+
}
|
|
813
|
+
},
|
|
814
|
+
"Initials#1054:32": {
|
|
815
|
+
"prop": "initials"
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
},
|
|
773
819
|
"126:22724": {
|
|
774
820
|
"figmaComponent": "Border",
|
|
775
821
|
"figmaNodeId": "126:22724",
|
|
@@ -1745,6 +1791,17 @@
|
|
|
1745
1791
|
},
|
|
1746
1792
|
"Icon#74:50": {
|
|
1747
1793
|
"prop": "icon"
|
|
1794
|
+
},
|
|
1795
|
+
"Show Ring#1009:0": {
|
|
1796
|
+
"prop": "ring"
|
|
1797
|
+
},
|
|
1798
|
+
"Show Top Indicator#1009:25": {
|
|
1799
|
+
"prop": "topIndicator",
|
|
1800
|
+
"note": "Figma needs a boolean because the intent lives on the nested Status Indicator instance; code takes the intent itself, so passing one is what shows the mark. The drawn instance is Intent=Primary with its check glyph, which is what `topIndicator=\"primary\"` renders."
|
|
1801
|
+
},
|
|
1802
|
+
"Show Bottom Indicator#1009:50": {
|
|
1803
|
+
"prop": "bottomIndicator",
|
|
1804
|
+
"note": "As the top indicator — the intent comes from the nested Status Indicator, which a boolean cannot carry. The drawn instance is Intent=Success with no glyph."
|
|
1748
1805
|
}
|
|
1749
1806
|
}
|
|
1750
1807
|
},
|
|
@@ -1786,6 +1843,41 @@
|
|
|
1786
1843
|
}
|
|
1787
1844
|
}
|
|
1788
1845
|
},
|
|
1846
|
+
"Avatar Gradient": {
|
|
1847
|
+
"figmaComponent": "Avatar Gradient",
|
|
1848
|
+
"figmaNodeId": "1054:305",
|
|
1849
|
+
"figmaPage": "Avatar",
|
|
1850
|
+
"component": "AvatarGradient",
|
|
1851
|
+
"import": "import { AvatarGradient } from 'ionbase-ui';",
|
|
1852
|
+
"docs": "components/avatar-gradient/index.html.md",
|
|
1853
|
+
"props": {
|
|
1854
|
+
"Size": {
|
|
1855
|
+
"prop": "size",
|
|
1856
|
+
"values": {
|
|
1857
|
+
"Mini": "mini",
|
|
1858
|
+
"Small": "sm",
|
|
1859
|
+
"Medium": "md",
|
|
1860
|
+
"Large": "lg"
|
|
1861
|
+
}
|
|
1862
|
+
},
|
|
1863
|
+
"Color": {
|
|
1864
|
+
"prop": "color",
|
|
1865
|
+
"values": {
|
|
1866
|
+
"Slate": "slate",
|
|
1867
|
+
"Blue": "blue",
|
|
1868
|
+
"Violet": "violet",
|
|
1869
|
+
"Pink": "pink",
|
|
1870
|
+
"Orange": "orange",
|
|
1871
|
+
"Green": "green",
|
|
1872
|
+
"Red": "red",
|
|
1873
|
+
"Light": "light"
|
|
1874
|
+
}
|
|
1875
|
+
},
|
|
1876
|
+
"Initials#1054:32": {
|
|
1877
|
+
"prop": "initials"
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
},
|
|
1789
1881
|
"Border": {
|
|
1790
1882
|
"figmaComponent": "Border",
|
|
1791
1883
|
"figmaNodeId": "126:22724",
|
|
@@ -2044,7 +2136,8 @@
|
|
|
2044
2136
|
"Menu Section Title": "No code counterpart yet. It is a heading inside a menu surface; nothing renders it.",
|
|
2045
2137
|
"Line": "An internal part of the Scroll Progress rail, not a component a caller composes.",
|
|
2046
2138
|
"Progress": "As Line — internal to ScrollProgress.",
|
|
2047
|
-
"Progress Heading": "As Line — internal to ScrollProgress."
|
|
2139
|
+
"Progress Heading": "As Line — internal to ScrollProgress.",
|
|
2140
|
+
"Status Indicator": "The dot Avatar draws in its corners, and nothing else composes it. Avatar owns both instances and exposes them as `topIndicator` / `bottomIndicator`, so there is no standalone component for a caller to place — the same call Line, Progress and Progress Heading get for being internal to ScrollProgress. Map it the day a screen needs a status dot away from an avatar."
|
|
2048
2141
|
},
|
|
2049
2142
|
"codeUnmapped": {
|
|
2050
2143
|
"Icon": "Figma has no Icon component; icons are vector assets dropped into slots. Icon is the React wrapper that gives any of them a size rung and an accessible label, so there is nothing on the Figma side to point at.",
|
package/dist/meta/Avatar.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "Avatar",
|
|
3
3
|
"source": "src/components/Avatar.tsx",
|
|
4
4
|
"propsType": "AvatarProps",
|
|
5
|
-
"description": "Avatar picks its content the way Figma's `Type` variant does, but by\nprecedence rather than by a prop: image, then initials, then icon. A caller\npassing `src` and `initials` gets the image with the initials as its alt\nfallback, which is what you want when the image 404s.\n\nA failed `src` falls through to initials (then icon) rather than leaving the\nbrowser's broken-image glyph — that is why initials exist alongside `src`.\n\nIt is a `<span>`, not a `<div>`, so it can sit inline beside text without the\ncaller fighting a block element.",
|
|
5
|
+
"description": "Avatar picks its content the way Figma's `Type` variant does, but by\nprecedence rather than by a prop: image, then initials, then icon. A caller\npassing `src` and `initials` gets the image with the initials as its alt\nfallback, which is what you want when the image 404s.\n\nA failed `src` falls through to initials (then icon) rather than leaving the\nbrowser's broken-image glyph — that is why initials exist alongside `src`.\n\nIt is a `<span>`, not a `<div>`, so it can sit inline beside text without the\ncaller fighting a block element.\n\nTHE MEDIA IS A CHILD, AND IT HAS TO BE\n\nThe box that clips the image is `.ion-avatar__media`, not the root. The root\ncannot clip: a corner indicator sits outside a circle's clip path and would\nbe sliced in half by the `overflow: hidden` that crops the photo. Figma\ndraws exactly this — an unclipped frame holding a clipped `Media` child and\nthe indicators as its siblings.",
|
|
6
6
|
"import": "import { Avatar } from 'ionbase-ui';",
|
|
7
7
|
"status": "stable",
|
|
8
8
|
"summary": "A person or entity marker. Content is chosen by precedence — image, then initials, then icon — not by a type prop.",
|
|
@@ -42,21 +42,67 @@
|
|
|
42
42
|
"square": {
|
|
43
43
|
"use": "organisations, projects, workspaces and other non-human entities"
|
|
44
44
|
}
|
|
45
|
+
},
|
|
46
|
+
"topIndicator": {
|
|
47
|
+
"neutral": {
|
|
48
|
+
"use": "no state, or a state this system has no colour for"
|
|
49
|
+
},
|
|
50
|
+
"primary": {
|
|
51
|
+
"use": "attention rather than health — a request, an invite, a mention"
|
|
52
|
+
},
|
|
53
|
+
"success": {
|
|
54
|
+
"use": "online, available, verified, complete"
|
|
55
|
+
},
|
|
56
|
+
"warning": {
|
|
57
|
+
"use": "away, idle, needs attention but is not broken"
|
|
58
|
+
},
|
|
59
|
+
"error": {
|
|
60
|
+
"use": "offline, blocked, failed"
|
|
61
|
+
},
|
|
62
|
+
"information": {
|
|
63
|
+
"use": "a neutral fact about the person — a role or a badge, not a state"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"bottomIndicator": {
|
|
67
|
+
"neutral": {
|
|
68
|
+
"use": "no state, or a state this system has no colour for"
|
|
69
|
+
},
|
|
70
|
+
"primary": {
|
|
71
|
+
"use": "attention rather than health — a request, an invite, a mention"
|
|
72
|
+
},
|
|
73
|
+
"success": {
|
|
74
|
+
"use": "online, available, verified, complete"
|
|
75
|
+
},
|
|
76
|
+
"warning": {
|
|
77
|
+
"use": "away, idle, needs attention but is not broken"
|
|
78
|
+
},
|
|
79
|
+
"error": {
|
|
80
|
+
"use": "offline, blocked, failed"
|
|
81
|
+
},
|
|
82
|
+
"information": {
|
|
83
|
+
"use": "a neutral fact about the person — a role or a badge, not a state"
|
|
84
|
+
}
|
|
45
85
|
}
|
|
46
86
|
},
|
|
47
87
|
"slots": {
|
|
48
88
|
"icon": {
|
|
49
89
|
"accepts": "Icon",
|
|
50
90
|
"note": "the last fallback — rendered only when there is neither `src` nor `initials`"
|
|
91
|
+
},
|
|
92
|
+
"topIndicatorIcon": {
|
|
93
|
+
"accepts": "Icon",
|
|
94
|
+
"note": "replaces the top indicator's default check glyph. The bottom indicator takes no glyph — Figma draws it bare, and at Mini it is 8px, which nothing legible fits inside."
|
|
51
95
|
}
|
|
52
96
|
},
|
|
53
97
|
"a11y": {
|
|
54
98
|
"guarantees": [
|
|
55
99
|
"`alt` falls back to `initials`, so the avatar is never announced as an unlabelled image",
|
|
56
|
-
"a `src` that fails to load falls through to initials, then icon — never the browser's broken-image glyph"
|
|
100
|
+
"a `src` that fails to load falls through to initials, then icon — never the browser's broken-image glyph",
|
|
101
|
+
"indicator labels are announced wherever they land: folded into the avatar's own `aria-label` when there is no image (the root is `role=\"img\"` and therefore a leaf), and carried as hidden text inside each indicator when there is"
|
|
57
102
|
],
|
|
58
103
|
"requires": [
|
|
59
|
-
"`alt` describing the person, not the picture — \"Ada Lovelace\", not \"profile photo\""
|
|
104
|
+
"`alt` describing the person, not the picture — \"Ada Lovelace\", not \"profile photo\"",
|
|
105
|
+
"`topIndicatorLabel` / `bottomIndicatorLabel` on any indicator that carries meaning. An indicator with no label is decorative, which is the right answer only when the same fact is already stated in text beside it."
|
|
60
106
|
]
|
|
61
107
|
},
|
|
62
108
|
"antiPatterns": [
|
|
@@ -68,43 +114,75 @@
|
|
|
68
114
|
"dont": "using Avatar as a button",
|
|
69
115
|
"do": "put the Avatar inside a Button or Link",
|
|
70
116
|
"why": "Avatar renders a <span> with no role and takes no focus"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"dont": "using an indicator's colour as the only carrier of a status",
|
|
120
|
+
"do": "pass `topIndicatorLabel` / `bottomIndicatorLabel`, or state the status in text nearby",
|
|
121
|
+
"why": "WCAG 1.4.1. A green dot and a red dot are the same dot to a screen reader, and to a good share of sighted users too"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"dont": "reaching for `ring` to mean selected or focused",
|
|
125
|
+
"why": "it is a static brand ring from the design, not a focus indicator. Focus belongs on whatever interactive element wraps the avatar, and that element draws its own"
|
|
71
126
|
}
|
|
72
127
|
],
|
|
73
128
|
"stylesheet": "src/styles/avatar.css",
|
|
74
129
|
"tokens": [
|
|
75
130
|
"--border-strong",
|
|
131
|
+
"--border-subtle",
|
|
76
132
|
"--border-width-default",
|
|
133
|
+
"--border-width-thick",
|
|
77
134
|
"--font-family-sans",
|
|
78
135
|
"--font-weight-medium",
|
|
136
|
+
"--font-weight-regular",
|
|
79
137
|
"--icon-size-2xs",
|
|
80
138
|
"--icon-size-lg",
|
|
81
139
|
"--icon-size-md",
|
|
82
140
|
"--icon-size-sm",
|
|
83
141
|
"--icon-tertiary",
|
|
142
|
+
"--ion-avatar-border",
|
|
84
143
|
"--ion-avatar-font-size",
|
|
144
|
+
"--ion-avatar-font-weight",
|
|
85
145
|
"--ion-avatar-group-overlap",
|
|
86
146
|
"--ion-avatar-icon",
|
|
147
|
+
"--ion-avatar-indicator-icon",
|
|
148
|
+
"--ion-avatar-indicator-ring",
|
|
149
|
+
"--ion-avatar-indicator-size",
|
|
87
150
|
"--ion-avatar-line-height",
|
|
88
151
|
"--ion-avatar-radius",
|
|
152
|
+
"--ion-avatar-ring-width",
|
|
89
153
|
"--ion-avatar-size",
|
|
154
|
+
"--ion-avatar-surface",
|
|
90
155
|
"--radius-full",
|
|
91
156
|
"--radius-md",
|
|
92
157
|
"--radius-sm",
|
|
93
158
|
"--radius-xs",
|
|
159
|
+
"--ring-offset",
|
|
160
|
+
"--spacing-12",
|
|
161
|
+
"--spacing-14",
|
|
162
|
+
"--spacing-16",
|
|
94
163
|
"--spacing-24",
|
|
95
164
|
"--spacing-32",
|
|
96
165
|
"--spacing-40",
|
|
97
166
|
"--spacing-48",
|
|
167
|
+
"--spacing-8",
|
|
168
|
+
"--surface-error",
|
|
169
|
+
"--surface-information",
|
|
170
|
+
"--surface-inverse",
|
|
98
171
|
"--surface-muted",
|
|
172
|
+
"--surface-placeholder",
|
|
173
|
+
"--surface-primary",
|
|
174
|
+
"--surface-success",
|
|
175
|
+
"--surface-warning",
|
|
176
|
+
"--text-on-color",
|
|
99
177
|
"--text-tertiary",
|
|
100
178
|
"--type-body",
|
|
179
|
+
"--type-body-lg",
|
|
180
|
+
"--type-body-lg-line-height",
|
|
101
181
|
"--type-body-line-height",
|
|
102
182
|
"--type-body-sm",
|
|
103
183
|
"--type-body-sm-line-height",
|
|
104
184
|
"--type-caption",
|
|
105
|
-
"--type-caption-line-height"
|
|
106
|
-
"--type-h5",
|
|
107
|
-
"--type-h5-line-height"
|
|
185
|
+
"--type-caption-line-height"
|
|
108
186
|
],
|
|
109
187
|
"props": {
|
|
110
188
|
"size": {
|
|
@@ -152,10 +230,62 @@
|
|
|
152
230
|
"required": false,
|
|
153
231
|
"origin": "own",
|
|
154
232
|
"description": "Figma's `Type=Icon`. Rendered when there is neither `src` nor `initials`."
|
|
233
|
+
},
|
|
234
|
+
"ring": {
|
|
235
|
+
"type": "boolean | undefined",
|
|
236
|
+
"required": false,
|
|
237
|
+
"origin": "own",
|
|
238
|
+
"description": "Figma's `Show Ring` — a 1px/2px brand ring drawn inside the avatar's edge."
|
|
239
|
+
},
|
|
240
|
+
"topIndicator": {
|
|
241
|
+
"type": "AvatarIndicatorIntent | undefined",
|
|
242
|
+
"required": false,
|
|
243
|
+
"origin": "own",
|
|
244
|
+
"description": "Figma's `Show Top Indicator`, as the intent of the `Status Indicator`\ninstance it holds. Top-right, and it carries a glyph: this is the corner\nthat says something was verified or requested, not who is online.",
|
|
245
|
+
"values": [
|
|
246
|
+
"neutral",
|
|
247
|
+
"primary",
|
|
248
|
+
"success",
|
|
249
|
+
"warning",
|
|
250
|
+
"error",
|
|
251
|
+
"information"
|
|
252
|
+
]
|
|
253
|
+
},
|
|
254
|
+
"topIndicatorIcon": {
|
|
255
|
+
"type": "React.ReactNode",
|
|
256
|
+
"required": false,
|
|
257
|
+
"origin": "own",
|
|
258
|
+
"description": "Replaces the top indicator's default check glyph."
|
|
259
|
+
},
|
|
260
|
+
"topIndicatorLabel": {
|
|
261
|
+
"type": "string | undefined",
|
|
262
|
+
"required": false,
|
|
263
|
+
"origin": "own",
|
|
264
|
+
"description": "Names the top indicator for assistive tech. Without it the mark is decorative."
|
|
265
|
+
},
|
|
266
|
+
"bottomIndicator": {
|
|
267
|
+
"type": "AvatarIndicatorIntent | undefined",
|
|
268
|
+
"required": false,
|
|
269
|
+
"origin": "own",
|
|
270
|
+
"description": "Figma's `Show Bottom Indicator`. Bottom-right and glyphless — the presence\ndot.",
|
|
271
|
+
"values": [
|
|
272
|
+
"neutral",
|
|
273
|
+
"primary",
|
|
274
|
+
"success",
|
|
275
|
+
"warning",
|
|
276
|
+
"error",
|
|
277
|
+
"information"
|
|
278
|
+
]
|
|
279
|
+
},
|
|
280
|
+
"bottomIndicatorLabel": {
|
|
281
|
+
"type": "string | undefined",
|
|
282
|
+
"required": false,
|
|
283
|
+
"origin": "own",
|
|
284
|
+
"description": "Names the bottom indicator for assistive tech. Colour alone is not a status."
|
|
155
285
|
}
|
|
156
286
|
},
|
|
157
287
|
"propCounts": {
|
|
158
|
-
"own":
|
|
288
|
+
"own": 12,
|
|
159
289
|
"aria": 0,
|
|
160
290
|
"dom": 278,
|
|
161
291
|
"other": 0
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "AvatarGradient",
|
|
3
|
+
"source": "src/components/AvatarGradient.tsx",
|
|
4
|
+
"propsType": "AvatarGradientProps",
|
|
5
|
+
"description": "AvatarGradient is Avatar's initials-only sibling: a lit, bevelled disc for\npeople who have no photo.\n\nIT IS A SEPARATE COMPONENT BECAUSE FIGMA DRAWS A SEPARATE COMPONENT, AND THE\nAXES ARE THE ARGUMENT\n\nIt has `Color`, which Avatar does not. It has no `Shape`, because a gradient\nthis directional only reads on a circle. It has no `Type`, because there is\nno image and no icon to fall back to — the initials are the whole content.\nFolding it into Avatar would mean a `color` prop that does nothing on three\nof Avatar's four states and a `shape` prop that this one has to reject.\n\nThere is no `icon` and no `src` fallback here on purpose. If you have a\nphoto, or nothing at all to put in the circle, that is `Avatar`.",
|
|
6
|
+
"import": "import { AvatarGradient } from 'ionbase-ui';",
|
|
7
|
+
"status": "stable",
|
|
8
|
+
"summary": "Avatar's initials-only sibling: a lit, bevelled disc in one of eight colours, for people with no photo.",
|
|
9
|
+
"useWhen": [
|
|
10
|
+
"someone has no profile image and the fallback should still look deliberate rather than empty",
|
|
11
|
+
"a list of people reads better when each one is a different colour — the colour is recognisable long before the letters are legible"
|
|
12
|
+
],
|
|
13
|
+
"useInstead": [
|
|
14
|
+
{
|
|
15
|
+
"when": "there is a photo, or there may be one later",
|
|
16
|
+
"use": "Avatar",
|
|
17
|
+
"why": "Avatar falls back from image to initials to icon on its own; this component has no image to fall back from"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"when": "the mark needs a square shape, an icon, a ring or a status indicator",
|
|
21
|
+
"use": "Avatar",
|
|
22
|
+
"why": "Figma draws none of those on this component, and a gradient this directional only reads on a circle"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"when": "the thing being marked is a status, count or category rather than a person",
|
|
26
|
+
"use": "Badge"
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"variants": {
|
|
30
|
+
"size": {
|
|
31
|
+
"mini": {
|
|
32
|
+
"use": "24px — inline beside body text, dense table rows"
|
|
33
|
+
},
|
|
34
|
+
"sm": {
|
|
35
|
+
"use": "32px — menu rows, compact lists"
|
|
36
|
+
},
|
|
37
|
+
"md": {
|
|
38
|
+
"use": "the default, 40px"
|
|
39
|
+
},
|
|
40
|
+
"lg": {
|
|
41
|
+
"use": "48px — profile headers and detail pages"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"color": {
|
|
45
|
+
"slate": {
|
|
46
|
+
"use": "the default. The one to reach for when nothing about the person suggests a colour"
|
|
47
|
+
},
|
|
48
|
+
"blue": {
|
|
49
|
+
"use": "as slate — one of seven, picked to tell people apart"
|
|
50
|
+
},
|
|
51
|
+
"violet": {
|
|
52
|
+
"use": "as slate"
|
|
53
|
+
},
|
|
54
|
+
"pink": {
|
|
55
|
+
"use": "as slate. See `a11y.knownIssues` before putting it behind text that matters"
|
|
56
|
+
},
|
|
57
|
+
"orange": {
|
|
58
|
+
"use": "as slate. See `a11y.knownIssues`"
|
|
59
|
+
},
|
|
60
|
+
"green": {
|
|
61
|
+
"use": "as slate. Not a status — a green disc here does not mean online, and Avatar's `bottomIndicator` is what does"
|
|
62
|
+
},
|
|
63
|
+
"red": {
|
|
64
|
+
"use": "as slate. Not an error — this is identity, not state. See `a11y.knownIssues`"
|
|
65
|
+
},
|
|
66
|
+
"light": {
|
|
67
|
+
"use": "dark text on a near-white disc, for dense surfaces where seven saturated circles would shout"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"a11y": {
|
|
72
|
+
"guarantees": [
|
|
73
|
+
"`alt` falls back to `initials`, so the disc is never announced as an unlabelled image"
|
|
74
|
+
],
|
|
75
|
+
"requires": [
|
|
76
|
+
"`alt` naming the person — \"Ada Lovelace\", not \"AB\". Two letters are not a name, and that is all a screen reader would otherwise get"
|
|
77
|
+
],
|
|
78
|
+
"notes": [
|
|
79
|
+
"Colour is decoration here, never information. Nothing about which of the eight a person gets should be recoverable, because a screen reader cannot recover it."
|
|
80
|
+
],
|
|
81
|
+
"knownIssues": [
|
|
82
|
+
{
|
|
83
|
+
"pairing": "--color-base-white on --color-pink-500",
|
|
84
|
+
"mode": "Light",
|
|
85
|
+
"ratio": 3.53,
|
|
86
|
+
"required": 4.5,
|
|
87
|
+
"sc": "1.4.3",
|
|
88
|
+
"states": [
|
|
89
|
+
"default"
|
|
90
|
+
],
|
|
91
|
+
"status": "known, not yet fixed"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"pairing": "--color-base-white on --color-orange-500",
|
|
95
|
+
"mode": "Light",
|
|
96
|
+
"ratio": 3.6,
|
|
97
|
+
"required": 4.5,
|
|
98
|
+
"sc": "1.4.3",
|
|
99
|
+
"states": [
|
|
100
|
+
"default"
|
|
101
|
+
],
|
|
102
|
+
"status": "known, not yet fixed"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"pairing": "--color-base-white on --color-green-500",
|
|
106
|
+
"mode": "Light",
|
|
107
|
+
"ratio": 3.69,
|
|
108
|
+
"required": 4.5,
|
|
109
|
+
"sc": "1.4.3",
|
|
110
|
+
"states": [
|
|
111
|
+
"default"
|
|
112
|
+
],
|
|
113
|
+
"status": "known, not yet fixed"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"pairing": "--color-base-white on --color-red-500",
|
|
117
|
+
"mode": "Light",
|
|
118
|
+
"ratio": 3.78,
|
|
119
|
+
"required": 4.5,
|
|
120
|
+
"sc": "1.4.3",
|
|
121
|
+
"states": [
|
|
122
|
+
"default"
|
|
123
|
+
],
|
|
124
|
+
"status": "known, not yet fixed"
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"antiPatterns": [
|
|
129
|
+
{
|
|
130
|
+
"dont": "picking the colour from a status — green for online, red for failing",
|
|
131
|
+
"do": "use Avatar's `bottomIndicator`, or say it in text",
|
|
132
|
+
"why": "the colour is identity, not state. It is also invisible to assistive tech, so a status expressed only this way is not expressed at all"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"dont": "hashing a user id to a colour and treating it as stable",
|
|
136
|
+
"do": "store the chosen colour with the person, the way you would store their avatar",
|
|
137
|
+
"why": "the palette has eight entries and any hash of it will collide; worse, it silently reassigns everyone the day the palette or the hash changes"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"dont": "passing more than two letters",
|
|
141
|
+
"why": "Figma draws two. Three fit at 48 and clip at 24, so it looks correct in the story and wrong in the table"
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
"stylesheet": "src/styles/avatar-gradient.css",
|
|
145
|
+
"tokens": [
|
|
146
|
+
"--color-base-white",
|
|
147
|
+
"--color-blue-500",
|
|
148
|
+
"--color-blue-600",
|
|
149
|
+
"--color-gray-100",
|
|
150
|
+
"--color-gray-200",
|
|
151
|
+
"--color-gray-500",
|
|
152
|
+
"--color-gray-600",
|
|
153
|
+
"--color-gray-900",
|
|
154
|
+
"--color-green-500",
|
|
155
|
+
"--color-green-600",
|
|
156
|
+
"--color-orange-500",
|
|
157
|
+
"--color-orange-600",
|
|
158
|
+
"--color-pink-500",
|
|
159
|
+
"--color-pink-600",
|
|
160
|
+
"--color-purple-500",
|
|
161
|
+
"--color-purple-600",
|
|
162
|
+
"--color-red-500",
|
|
163
|
+
"--color-red-600",
|
|
164
|
+
"--font-family-sans",
|
|
165
|
+
"--font-weight-medium",
|
|
166
|
+
"--font-weight-regular",
|
|
167
|
+
"--ion-avatar-gradient-elevation",
|
|
168
|
+
"--ion-avatar-gradient-font-size",
|
|
169
|
+
"--ion-avatar-gradient-font-weight",
|
|
170
|
+
"--ion-avatar-gradient-from",
|
|
171
|
+
"--ion-avatar-gradient-line-height",
|
|
172
|
+
"--ion-avatar-gradient-sheen",
|
|
173
|
+
"--ion-avatar-gradient-sheen-mid",
|
|
174
|
+
"--ion-avatar-gradient-size",
|
|
175
|
+
"--ion-avatar-gradient-text",
|
|
176
|
+
"--ion-avatar-gradient-to",
|
|
177
|
+
"--radius-full",
|
|
178
|
+
"--spacing-24",
|
|
179
|
+
"--spacing-32",
|
|
180
|
+
"--spacing-40",
|
|
181
|
+
"--spacing-48",
|
|
182
|
+
"--type-body",
|
|
183
|
+
"--type-body-lg",
|
|
184
|
+
"--type-body-lg-line-height",
|
|
185
|
+
"--type-body-line-height",
|
|
186
|
+
"--type-body-sm",
|
|
187
|
+
"--type-body-sm-line-height",
|
|
188
|
+
"--type-caption",
|
|
189
|
+
"--type-caption-line-height"
|
|
190
|
+
],
|
|
191
|
+
"props": {
|
|
192
|
+
"size": {
|
|
193
|
+
"type": "AvatarSize | undefined",
|
|
194
|
+
"required": false,
|
|
195
|
+
"origin": "own",
|
|
196
|
+
"description": "Matches the Figma `Size` variant: Mini 24, Small 32, Medium 40, Large 48.",
|
|
197
|
+
"values": [
|
|
198
|
+
"sm",
|
|
199
|
+
"md",
|
|
200
|
+
"lg",
|
|
201
|
+
"mini"
|
|
202
|
+
]
|
|
203
|
+
},
|
|
204
|
+
"color": {
|
|
205
|
+
"type": "AvatarGradientColor | undefined",
|
|
206
|
+
"required": false,
|
|
207
|
+
"origin": "own",
|
|
208
|
+
"description": "Matches the Figma `Color` variant.",
|
|
209
|
+
"values": [
|
|
210
|
+
"slate",
|
|
211
|
+
"blue",
|
|
212
|
+
"violet",
|
|
213
|
+
"pink",
|
|
214
|
+
"orange",
|
|
215
|
+
"green",
|
|
216
|
+
"red",
|
|
217
|
+
"light"
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
"initials": {
|
|
221
|
+
"type": "string | undefined",
|
|
222
|
+
"required": false,
|
|
223
|
+
"origin": "own",
|
|
224
|
+
"description": "The two letters. Figma's `Initials`."
|
|
225
|
+
},
|
|
226
|
+
"alt": {
|
|
227
|
+
"type": "string | undefined",
|
|
228
|
+
"required": false,
|
|
229
|
+
"origin": "own",
|
|
230
|
+
"description": "Describes the person, not the picture — \"Ada Lovelace\", not \"avatar\".\nWithout it the initials are announced as the letters they are, which is\nnot a name."
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"propCounts": {
|
|
234
|
+
"own": 4,
|
|
235
|
+
"aria": 0,
|
|
236
|
+
"dom": 277,
|
|
237
|
+
"other": 0
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -64,38 +64,61 @@
|
|
|
64
64
|
"stylesheet": "src/styles/avatar.css",
|
|
65
65
|
"tokens": [
|
|
66
66
|
"--border-strong",
|
|
67
|
+
"--border-subtle",
|
|
67
68
|
"--border-width-default",
|
|
69
|
+
"--border-width-thick",
|
|
68
70
|
"--font-family-sans",
|
|
69
71
|
"--font-weight-medium",
|
|
72
|
+
"--font-weight-regular",
|
|
70
73
|
"--icon-size-2xs",
|
|
71
74
|
"--icon-size-lg",
|
|
72
75
|
"--icon-size-md",
|
|
73
76
|
"--icon-size-sm",
|
|
74
77
|
"--icon-tertiary",
|
|
78
|
+
"--ion-avatar-border",
|
|
75
79
|
"--ion-avatar-font-size",
|
|
80
|
+
"--ion-avatar-font-weight",
|
|
76
81
|
"--ion-avatar-group-overlap",
|
|
77
82
|
"--ion-avatar-icon",
|
|
83
|
+
"--ion-avatar-indicator-icon",
|
|
84
|
+
"--ion-avatar-indicator-ring",
|
|
85
|
+
"--ion-avatar-indicator-size",
|
|
78
86
|
"--ion-avatar-line-height",
|
|
79
87
|
"--ion-avatar-radius",
|
|
88
|
+
"--ion-avatar-ring-width",
|
|
80
89
|
"--ion-avatar-size",
|
|
90
|
+
"--ion-avatar-surface",
|
|
81
91
|
"--radius-full",
|
|
82
92
|
"--radius-md",
|
|
83
93
|
"--radius-sm",
|
|
84
94
|
"--radius-xs",
|
|
95
|
+
"--ring-offset",
|
|
96
|
+
"--spacing-12",
|
|
97
|
+
"--spacing-14",
|
|
98
|
+
"--spacing-16",
|
|
85
99
|
"--spacing-24",
|
|
86
100
|
"--spacing-32",
|
|
87
101
|
"--spacing-40",
|
|
88
102
|
"--spacing-48",
|
|
103
|
+
"--spacing-8",
|
|
104
|
+
"--surface-error",
|
|
105
|
+
"--surface-information",
|
|
106
|
+
"--surface-inverse",
|
|
89
107
|
"--surface-muted",
|
|
108
|
+
"--surface-placeholder",
|
|
109
|
+
"--surface-primary",
|
|
110
|
+
"--surface-success",
|
|
111
|
+
"--surface-warning",
|
|
112
|
+
"--text-on-color",
|
|
90
113
|
"--text-tertiary",
|
|
91
114
|
"--type-body",
|
|
115
|
+
"--type-body-lg",
|
|
116
|
+
"--type-body-lg-line-height",
|
|
92
117
|
"--type-body-line-height",
|
|
93
118
|
"--type-body-sm",
|
|
94
119
|
"--type-body-sm-line-height",
|
|
95
120
|
"--type-caption",
|
|
96
|
-
"--type-caption-line-height"
|
|
97
|
-
"--type-h5",
|
|
98
|
-
"--type-h5-line-height"
|
|
121
|
+
"--type-caption-line-height"
|
|
99
122
|
],
|
|
100
123
|
"props": {
|
|
101
124
|
"size": {
|