@sableui/react 0.4.0 → 0.6.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/AGENTS.md +2 -2
- package/CHANGELOG.md +189 -0
- package/CONTRIBUTING.md +1 -1
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/bin/sable-lint.mjs +2 -1
- package/bin/sable.mjs +3 -2
- package/dist/{_contract-Cn3lUpih.d.ts → _contract-C2M1SjBf.d.ts} +20 -2
- package/dist/blocks/index.js +21 -53
- package/dist/{chunk-CCUDFLU3.js → chunk-7QSDYBXO.js} +652 -38
- package/dist/{chunk-FBSL2LZQ.js → chunk-EDXLM7YL.js} +11 -30
- package/dist/chunk-FKZXAFIY.js +12 -0
- package/dist/{chunk-OQACYT2D.js → chunk-NETUFI34.js} +23 -1
- package/dist/{chunk-NGBDP3BM.js → chunk-QJ6FWTUG.js} +1 -1
- package/dist/{chunk-D2ETQBHV.js → chunk-VVW4UCDI.js} +5 -5
- package/dist/{chunk-TH563WLW.js → chunk-XAZGMY73.js} +10 -3
- package/dist/foundations/index.js +6 -0
- package/dist/icon/index.js +2 -2
- package/dist/icon-sets/brands.d.ts +0 -27
- package/dist/icon-sets/brands.js +7 -1
- package/dist/icon-sets/reicon.js +1 -1
- package/dist/index.d.ts +164 -23
- package/dist/index.js +116 -29
- package/dist/ios/index.d.ts +6 -1
- package/dist/ios/index.js +4 -3
- package/dist/ios/styles.css +9 -0
- package/dist/patterns/index.js +24 -30
- package/dist/presets/index.d.ts +108 -5
- package/dist/presets/index.js +239 -6
- package/dist/styles.css +57 -0
- package/dist/{theme-provider-CGeiusgu.d.ts → theme-provider-kQszHYji.d.ts} +1 -1
- package/dist/themes/atelier/atelier.css +31 -3
- package/dist/themes/atelier/index.d.ts +1 -1
- package/dist/themes/ember/ember.css +38 -7
- package/dist/themes/ember/index.d.ts +1 -1
- package/dist/themes/index.d.ts +2 -2
- package/dist/themes/meridian/index.d.ts +1 -1
- package/dist/themes/meridian/meridian.css +33 -4
- package/dist/themes/poppy/index.d.ts +1 -1
- package/dist/themes/poppy/poppy.css +28 -0
- package/dist/themes/prism/index.d.ts +1 -1
- package/dist/themes/prism/prism.css +26 -0
- package/dist/themes/sable/index.d.ts +1 -1
- package/dist/themes/sable/sable.css +24 -11
- package/dist/themes/verdant/index.d.ts +1 -1
- package/dist/themes/verdant/verdant.css +34 -4
- package/llms/blocks/danger-zone-block.md +1 -1
- package/llms/blocks/event-card-block.md +1 -1
- package/llms/blocks/feedback-form-block.md +1 -1
- package/llms/blocks/invite-team-block.md +1 -1
- package/llms/blocks/media-player-block.md +3 -16
- package/llms/blocks/notification-list-block.md +3 -3
- package/llms/blocks/product-card-block.md +3 -17
- package/llms/blocks/profile-card-block.md +4 -17
- package/llms/blocks/profile-form-block.md +2 -2
- package/llms/blocks/team-members-block.md +1 -1
- package/llms/blocks/testimonial-block.md +1 -1
- package/llms/components/avatar.md +13 -1
- package/llms/components/card.md +1 -1
- package/llms/components/gradient-art.md +80 -0
- package/llms/components/sheet.md +1 -1
- package/llms/components/tabs.md +1 -1
- package/llms/components/toast.md +12 -1
- package/llms/foundations/accessibility.md +1 -0
- package/llms/patterns/auth-page.md +5 -19
- package/llms/patterns/brand-landing-page.md +1 -1
- package/llms/patterns/chat-panel.md +1 -1
- package/llms/patterns/dashboard-page.md +4 -4
- package/llms/patterns/feed-page.md +4 -4
- package/llms/patterns/landing-page.md +1 -1
- package/llms/patterns/list-detail-page.md +3 -2
- package/llms/patterns/settings-page.md +3 -3
- package/llms/reference/token-intent.md +9 -1
- package/llms/tasks/auditing-an-app.md +6 -0
- package/llms/tasks/building-an-ios-prototype.md +3 -1
- package/llms/tasks/converting-an-app.md +1 -1
- package/llms/tasks/creating-a-preset.md +24 -0
- package/llms/tasks/reviewing-a-design.md +9 -0
- package/llms/tasks/using-presets.md +6 -1
- package/llms-full.txt +214 -106
- package/llms.txt +4 -3
- package/package.json +9 -7
- package/registry.json +310 -32
- package/scripts/design-presets.mjs +27 -4
- package/scripts/grade.mjs +2 -1
- package/scripts/label-contrast.mjs +138 -0
- package/scripts/lint-rules.mjs +23 -2
- package/skills/sable/SKILL.md +3 -1
- package/targets/ios.json +18 -2
- package/third-party/paper-shaders-LICENSE.txt +201 -0
- package/third-party/paper-shaders-NOTICE.txt +5 -0
|
@@ -143,14 +143,18 @@
|
|
|
143
143
|
--accent: oklch(57.5% 0.2 31.5);
|
|
144
144
|
--accent-foreground: oklch(99% 0.004 91.5);
|
|
145
145
|
--destructive: oklch(48% 0.2 25);
|
|
146
|
+
--destructive-text: oklch(48% 0.2 25);
|
|
146
147
|
--destructive-foreground: oklch(94.6% 0.0083 91.48);
|
|
147
148
|
--success: oklch(43% 0.1 165);
|
|
149
|
+
--success-text: oklch(43% 0.1 165);
|
|
148
150
|
--success-foreground: oklch(94.6% 0.0083 91.48);
|
|
149
151
|
--tone-standard: oklch(40% 0.07 130);
|
|
150
152
|
--tone-collectible: oklch(46% 0.14 285);
|
|
151
153
|
--tone-edition: oklch(45% 0.15 309);
|
|
152
154
|
--tone-info: oklch(48% 0.13 250);
|
|
155
|
+
--tone-info-text: oklch(48% 0.13 250);
|
|
153
156
|
--tone-warning: oklch(50% 0.12 52);
|
|
157
|
+
--tone-warning-text: oklch(50% 0.12 52);
|
|
154
158
|
--highlight: oklch(57.5% 0.2 31.5);
|
|
155
159
|
/* Ember as TEXT (eyebrow numbers, arrows). The fill ember carries white ink
|
|
156
160
|
but is too light to be read as ink itself (4.1:1 on the sand); this is the
|
|
@@ -230,15 +234,20 @@
|
|
|
230
234
|
--secondary-foreground: oklch(94.3% 0.0042 91.45);
|
|
231
235
|
--accent: oklch(57.5% 0.2 31.5);
|
|
232
236
|
--accent-foreground: oklch(99% 0.004 91.5);
|
|
233
|
-
|
|
234
|
-
--destructive
|
|
235
|
-
--
|
|
237
|
+
/* Status colours come in two: the fill carries a label (destructive sits low under white ink), and `-text` is the same colour drawn on a surface — text, borders, rings — kept light enough to read on a dark card. */
|
|
238
|
+
--destructive: oklch(55% 0.21 25);
|
|
239
|
+
--destructive-text: oklch(78.5% 0.122 25);
|
|
240
|
+
--destructive-foreground: oklch(94.6% 0.0083 91.48);
|
|
241
|
+
--success: oklch(75.5% 0.12 165);
|
|
242
|
+
--success-text: oklch(75.5% 0.12 165);
|
|
236
243
|
--success-foreground: oklch(22.46% 0.0094 107.13);
|
|
237
244
|
--tone-standard: oklch(70% 0.09 130);
|
|
238
245
|
--tone-collectible: oklch(70% 0.12 285);
|
|
239
246
|
--tone-edition: oklch(68% 0.14 309);
|
|
240
|
-
--tone-info: oklch(
|
|
247
|
+
--tone-info: oklch(76.5% 0.11 250);
|
|
248
|
+
--tone-info-text: oklch(76.5% 0.11 250);
|
|
241
249
|
--tone-warning: oklch(78% 0.13 60);
|
|
250
|
+
--tone-warning-text: oklch(78% 0.13 60);
|
|
242
251
|
--highlight: oklch(57.5% 0.2 31.5);
|
|
243
252
|
--meridian-ember-ink: oklch(72% 0.17 33); /* 6.4:1 on the dark ground */
|
|
244
253
|
--tone-standard-foreground: oklch(22.46% 0.0094 107.13);
|
|
@@ -258,3 +267,23 @@
|
|
|
258
267
|
--sidebar-border: oklch(34.69% 0.0186 98.75);
|
|
259
268
|
--sidebar-ring: oklch(65.11% 0.2063 32.56);
|
|
260
269
|
}
|
|
270
|
+
|
|
271
|
+
/* Art direction for generated imagery (GradientArt; ART_TOKENS in
|
|
272
|
+
themes/_contract.ts). Grainy monochrome photographic grounds (art-direction.md): soft
|
|
273
|
+
mid-grey tones, slightly lifted blacks, film grain — and none of the orange
|
|
274
|
+
accent, which belongs to typography and controls. Greys carry the ink's
|
|
275
|
+
warm cast.
|
|
276
|
+
Declared on every arm so a .dark or .light island derives from its own
|
|
277
|
+
tokens rather than inheriting this scope's resolved colours. */
|
|
278
|
+
[data-sable-theme="meridian"],
|
|
279
|
+
[data-sable-theme="meridian"] .dark,
|
|
280
|
+
[data-sable-theme="meridian"] .light {
|
|
281
|
+
--theme-art-1: oklch(from var(--foreground) 0.88 0.008 h);
|
|
282
|
+
--theme-art-2: oklch(from var(--foreground) 0.62 0.006 h);
|
|
283
|
+
--theme-art-3: oklch(from var(--foreground) 0.38 0.008 h);
|
|
284
|
+
--theme-art-4: oklch(from var(--foreground) 0.74 0.006 h);
|
|
285
|
+
--theme-art-ground: oklch(from var(--foreground) 0.28 0.008 h);
|
|
286
|
+
--theme-art-warp: 0.4;
|
|
287
|
+
--theme-art-softness: 0.92;
|
|
288
|
+
--theme-art-grain: 0.55;
|
|
289
|
+
}
|
|
@@ -79,14 +79,18 @@
|
|
|
79
79
|
--accent: oklch(87.83223% 0.046910 333.7803);
|
|
80
80
|
--accent-foreground: oklch(27.91553% 0.070219 323.8141);
|
|
81
81
|
--destructive: oklch(51.71923% 0.183122 13.5471);
|
|
82
|
+
--destructive-text: oklch(51.71923% 0.183122 13.5471);
|
|
82
83
|
--destructive-foreground: oklch(100.00000% 0.000000 89.8756);
|
|
83
84
|
--success: oklch(44.82304% 0.085811 158.9570);
|
|
85
|
+
--success-text: oklch(44.82304% 0.085811 158.9570);
|
|
84
86
|
--success-foreground: oklch(100.00000% 0.000000 89.8756);
|
|
85
87
|
--tone-standard: oklch(45.04846% 0.050543 265.7140);
|
|
86
88
|
--tone-collectible: oklch(44.74226% 0.160756 301.6122);
|
|
87
89
|
--tone-edition: oklch(48.37678% 0.151106 344.8805);
|
|
88
90
|
--tone-info: oklch(47.10428% 0.117665 253.7666);
|
|
91
|
+
--tone-info-text: oklch(47.10428% 0.117665 253.7666);
|
|
89
92
|
--tone-warning: oklch(48.46763% 0.102195 75.1153);
|
|
93
|
+
--tone-warning-text: oklch(48.46763% 0.102195 75.1153);
|
|
90
94
|
--highlight: oklch(86.64637% 0.118528 86.6791);
|
|
91
95
|
--tone-standard-foreground: oklch(100.00000% 0.000000 89.8756);
|
|
92
96
|
--tone-collectible-foreground: oklch(100.00000% 0.000000 89.8756);
|
|
@@ -162,14 +166,18 @@
|
|
|
162
166
|
--accent: oklch(45.76059% 0.076204 319.6398);
|
|
163
167
|
--accent-foreground: oklch(96.99788% 0.031195 84.5922);
|
|
164
168
|
--destructive: oklch(80.09751% 0.118613 6.8786);
|
|
169
|
+
--destructive-text: oklch(80.09751% 0.118613 6.8786);
|
|
165
170
|
--destructive-foreground: oklch(27.91553% 0.070219 323.8141);
|
|
166
171
|
--success: oklch(83.08052% 0.080357 158.1865);
|
|
172
|
+
--success-text: oklch(83.08052% 0.080357 158.1865);
|
|
167
173
|
--success-foreground: oklch(27.91553% 0.070219 323.8141);
|
|
168
174
|
--tone-standard: oklch(82.64273% 0.059760 270.2687);
|
|
169
175
|
--tone-collectible: oklch(81.83406% 0.094036 306.4836);
|
|
170
176
|
--tone-edition: oklch(82.56509% 0.096677 343.4480);
|
|
171
177
|
--tone-info: oklch(85.19509% 0.074997 252.9332);
|
|
178
|
+
--tone-info-text: oklch(85.19509% 0.074997 252.9332);
|
|
172
179
|
--tone-warning: oklch(86.64637% 0.118528 86.6791);
|
|
180
|
+
--tone-warning-text: oklch(86.64637% 0.118528 86.6791);
|
|
173
181
|
--highlight: oklch(86.64637% 0.118528 86.6791);
|
|
174
182
|
--highlight-foreground: oklch(27.91553% 0.070219 323.8141);
|
|
175
183
|
--overlay: oklch(13.55719% 0.032037 309.1234);
|
|
@@ -188,3 +196,23 @@
|
|
|
188
196
|
--tone-info-foreground: oklch(27.91553% 0.070219 323.8141);
|
|
189
197
|
--tone-warning-foreground: oklch(27.91553% 0.070219 323.8141);
|
|
190
198
|
}
|
|
199
|
+
|
|
200
|
+
/* Art direction for generated imagery (GradientArt; ART_TOKENS in
|
|
201
|
+
themes/_contract.ts). Playful: candy colours in crisp, wobbly blobs — sunny yellow,
|
|
202
|
+
bubblegum, grape, sky and a mint the palette lacks. Low softness keeps each
|
|
203
|
+
blob its own colour instead of melting into one.
|
|
204
|
+
Declared on every arm so a .dark or .light island derives from its own
|
|
205
|
+
tokens rather than inheriting this scope's resolved colours. */
|
|
206
|
+
[data-sable-theme="poppy"],
|
|
207
|
+
[data-sable-theme="poppy"] .dark,
|
|
208
|
+
[data-sable-theme="poppy"] .light {
|
|
209
|
+
--theme-art-1: oklch(from var(--highlight) 0.88 0.15 h);
|
|
210
|
+
--theme-art-2: oklch(from var(--tone-edition) 0.72 0.19 h);
|
|
211
|
+
--theme-art-3: oklch(from var(--tone-collectible) 0.64 0.2 h);
|
|
212
|
+
--theme-art-4: oklch(from var(--tone-info) 0.76 0.13 h);
|
|
213
|
+
--theme-art-5: oklch(0.85 0.12 168);
|
|
214
|
+
--theme-art-ground: oklch(from var(--tone-collectible) 0.45 0.16 h);
|
|
215
|
+
--theme-art-warp: 1;
|
|
216
|
+
--theme-art-softness: 0.5;
|
|
217
|
+
--theme-art-grain: 0;
|
|
218
|
+
}
|
|
@@ -79,14 +79,18 @@
|
|
|
79
79
|
--accent: oklch(90.43693% 0.027753 253.2434);
|
|
80
80
|
--accent-foreground: oklch(26.55994% 0.047330 255.4106);
|
|
81
81
|
--destructive: oklch(51.62734% 0.183834 19.4551);
|
|
82
|
+
--destructive-text: oklch(51.62734% 0.183834 19.4551);
|
|
82
83
|
--destructive-foreground: oklch(100.00000% 0.000000 89.8756);
|
|
83
84
|
--success: oklch(45.54384% 0.081395 166.9049);
|
|
85
|
+
--success-text: oklch(45.54384% 0.081395 166.9049);
|
|
84
86
|
--success-foreground: oklch(100.00000% 0.000000 89.8756);
|
|
85
87
|
--tone-standard: oklch(44.17719% 0.043085 257.4210);
|
|
86
88
|
--tone-collectible: oklch(45.52411% 0.167785 299.9667);
|
|
87
89
|
--tone-edition: oklch(46.78672% 0.141265 343.5094);
|
|
88
90
|
--tone-info: oklch(46.18475% 0.139719 259.8084);
|
|
91
|
+
--tone-info-text: oklch(46.18475% 0.139719 259.8084);
|
|
89
92
|
--tone-warning: oklch(48.72795% 0.103396 73.7587);
|
|
93
|
+
--tone-warning-text: oklch(48.72795% 0.103396 73.7587);
|
|
90
94
|
--highlight: oklch(87.60451% 0.060193 266.4847);
|
|
91
95
|
--tone-standard-foreground: oklch(100.00000% 0.000000 89.8756);
|
|
92
96
|
--tone-collectible-foreground: oklch(100.00000% 0.000000 89.8756);
|
|
@@ -158,14 +162,18 @@
|
|
|
158
162
|
--accent: oklch(44.66150% 0.052212 253.9323);
|
|
159
163
|
--accent-foreground: oklch(96.50786% 0.016545 259.4182);
|
|
160
164
|
--destructive: oklch(79.47983% 0.122016 8.7083);
|
|
165
|
+
--destructive-text: oklch(79.47983% 0.122016 8.7083);
|
|
161
166
|
--destructive-foreground: oklch(25.20350% 0.047984 259.9119);
|
|
162
167
|
--success: oklch(82.23776% 0.085532 165.8233);
|
|
168
|
+
--success-text: oklch(82.23776% 0.085532 165.8233);
|
|
163
169
|
--success-foreground: oklch(25.20350% 0.047984 259.9119);
|
|
164
170
|
--tone-standard: oklch(82.02131% 0.046953 256.4806);
|
|
165
171
|
--tone-collectible: oklch(77.28561% 0.118760 301.0442);
|
|
166
172
|
--tone-edition: oklch(80.44093% 0.102099 341.6738);
|
|
167
173
|
--tone-info: oklch(80.57243% 0.097939 259.4939);
|
|
174
|
+
--tone-info-text: oklch(80.57243% 0.097939 259.4939);
|
|
168
175
|
--tone-warning: oklch(84.66478% 0.108333 84.1636);
|
|
176
|
+
--tone-warning-text: oklch(84.66478% 0.108333 84.1636);
|
|
169
177
|
--highlight: oklch(82.28684% 0.088371 261.1846);
|
|
170
178
|
--highlight-foreground: oklch(25.20350% 0.047984 259.9119);
|
|
171
179
|
--overlay: oklch(14.80476% 0.014876 256.6891);
|
|
@@ -184,3 +192,21 @@
|
|
|
184
192
|
--tone-info-foreground: oklch(25.20350% 0.047984 259.9119);
|
|
185
193
|
--tone-warning-foreground: oklch(25.20350% 0.047984 259.9119);
|
|
186
194
|
}
|
|
195
|
+
|
|
196
|
+
/* Art direction for generated imagery (GradientArt; ART_TOKENS in
|
|
197
|
+
themes/_contract.ts). Long soft airbrushed fields, pink to violet to periwinkle, with one
|
|
198
|
+
vivid indigo (art-direction.md). Diffuse, sourceless glow; no grain.
|
|
199
|
+
Declared on every arm so a .dark or .light island derives from its own
|
|
200
|
+
tokens rather than inheriting this scope's resolved colours. */
|
|
201
|
+
[data-sable-theme="prism"],
|
|
202
|
+
[data-sable-theme="prism"] .dark,
|
|
203
|
+
[data-sable-theme="prism"] .light {
|
|
204
|
+
--theme-art-1: oklch(from var(--tone-edition) 0.83 0.1 h);
|
|
205
|
+
--theme-art-2: oklch(from var(--tone-collectible) 0.68 0.16 h);
|
|
206
|
+
--theme-art-3: oklch(from var(--tone-info) 0.82 0.09 h);
|
|
207
|
+
--theme-art-4: oklch(from var(--primary) 0.55 0.2 h);
|
|
208
|
+
--theme-art-ground: oklch(from var(--primary) 0.35 0.14 h);
|
|
209
|
+
--theme-art-warp: 0.45;
|
|
210
|
+
--theme-art-softness: 1;
|
|
211
|
+
--theme-art-grain: 0;
|
|
212
|
+
}
|
|
@@ -220,8 +220,12 @@
|
|
|
220
220
|
--tone-standard-dark: oklch(78% 0.205 168);
|
|
221
221
|
--tone-info-dark: oklch(76% 0.165 235);
|
|
222
222
|
--tone-warning-dark: oklch(82% 0.165 65);
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
/* A labelled fill: dropped to where white reads (4.8:1, Lc 72) rather than
|
|
224
|
+
lifted to where dark ink reads muddy (Lc 46). */
|
|
225
|
+
--highlight-dark: oklch(57% 0.28 318);
|
|
226
|
+
/* Status colours come in two: the fill carries a label (destructive sits low under white ink), and `-text` is the same colour drawn on a surface — text, borders, rings — kept light enough to read on a dark card. */
|
|
227
|
+
--destructive-dark: oklch(77.5% 0.129 25);
|
|
228
|
+
--destructive-fill-dark: oklch(59% 0.225 25);
|
|
225
229
|
/* Success twins the standard tone's dark value today (same caribbean-green),
|
|
226
230
|
but is a SEPARATE token a theme can retune independently of the post-type. */
|
|
227
231
|
--success-dark: oklch(78% 0.205 168);
|
|
@@ -245,8 +249,10 @@
|
|
|
245
249
|
--accent: var(--zinc-100);
|
|
246
250
|
--accent-foreground: var(--zinc-950);
|
|
247
251
|
--destructive: var(--torch-red-600);
|
|
252
|
+
--destructive-text: var(--torch-red-600);
|
|
248
253
|
--destructive-foreground: var(--white);
|
|
249
254
|
--success: var(--caribbean-green-600);
|
|
255
|
+
--success-text: var(--caribbean-green-600);
|
|
250
256
|
--success-foreground: var(--zinc-50);
|
|
251
257
|
|
|
252
258
|
--border: var(--zinc-200);
|
|
@@ -263,7 +269,9 @@
|
|
|
263
269
|
--tone-collectible: var(--blue-gem-600);
|
|
264
270
|
--tone-edition: var(--purple-heart-700);
|
|
265
271
|
--tone-warning: var(--flush-orange-700);
|
|
272
|
+
--tone-warning-text: var(--flush-orange-700);
|
|
266
273
|
--tone-info: var(--azure-radiance-600);
|
|
274
|
+
--tone-info-text: var(--azure-radiance-700); /* a step under the fill: 600 is 4.46:1 as text on muted */
|
|
267
275
|
--highlight: var(--purple-heart-700);
|
|
268
276
|
|
|
269
277
|
/* Tone ink — light mode the tone fills resolve to dark 600/700 steps, so the
|
|
@@ -422,15 +430,18 @@
|
|
|
422
430
|
--muted-foreground: var(--zinc-400);
|
|
423
431
|
--accent: var(--zinc-800);
|
|
424
432
|
--accent-foreground: var(--zinc-50);
|
|
425
|
-
--destructive: var(--destructive-dark);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
+
--destructive: var(--destructive-fill-dark);
|
|
434
|
+
--destructive-text: var(--destructive-dark);
|
|
435
|
+
/* Dark destructive is two colours. The fill is a saturated red low enough for
|
|
436
|
+
white (4.6:1, Lc 75); dark ink on the old light-red fill passed the ratio but
|
|
437
|
+
read muddy (Lc 44). `text-destructive` and its borders and rings read
|
|
438
|
+
`--destructive-text`, the light, vibrant red that reads on the near-black
|
|
439
|
+
canvas. */
|
|
440
|
+
--destructive-foreground: var(--white);
|
|
441
|
+
/* Dark `--success` is a light, vibrant green that works as both fill and text,
|
|
442
|
+
so the legible ink flips to the dark neutral. */
|
|
433
443
|
--success: var(--success-dark);
|
|
444
|
+
--success-text: var(--success-dark);
|
|
434
445
|
--success-foreground: var(--zinc-950);
|
|
435
446
|
|
|
436
447
|
--border: oklch(from var(--zinc-700) l c h / 0.4);
|
|
@@ -444,7 +455,9 @@
|
|
|
444
455
|
--tone-collectible: var(--tone-collectible-dark);
|
|
445
456
|
--tone-edition: var(--tone-edition-dark);
|
|
446
457
|
--tone-warning: var(--tone-warning-dark);
|
|
458
|
+
--tone-warning-text: var(--tone-warning-dark);
|
|
447
459
|
--tone-info: var(--tone-info-dark);
|
|
460
|
+
--tone-info-text: var(--tone-info-dark);
|
|
448
461
|
--highlight: var(--highlight-dark);
|
|
449
462
|
|
|
450
463
|
/* Tone ink — dark mode the tone fills resolve to lighter dark-tuned values,
|
|
@@ -455,7 +468,7 @@
|
|
|
455
468
|
--tone-edition-foreground: var(--zinc-950);
|
|
456
469
|
--tone-warning-foreground: var(--zinc-950);
|
|
457
470
|
--tone-info-foreground: var(--zinc-950);
|
|
458
|
-
--highlight-foreground: var(--zinc-
|
|
471
|
+
--highlight-foreground: var(--zinc-50);
|
|
459
472
|
--overlay: var(--zinc-950);
|
|
460
473
|
--overlay-foreground: var(--zinc-50);
|
|
461
474
|
|
|
@@ -111,8 +111,10 @@
|
|
|
111
111
|
--accent: oklch(91.5% 0.014 170);
|
|
112
112
|
--accent-foreground: oklch(24% 0.035 175);
|
|
113
113
|
--destructive: oklch(51% 0.19 22);
|
|
114
|
+
--destructive-text: oklch(51% 0.19 22);
|
|
114
115
|
--destructive-foreground: oklch(98.6% 0.004 106);
|
|
115
116
|
--success: oklch(50% 0.11 173);
|
|
117
|
+
--success-text: oklch(50% 0.11 173);
|
|
116
118
|
--success-foreground: oklch(98.6% 0.004 106);
|
|
117
119
|
--tone-standard: oklch(49% 0.018 175);
|
|
118
120
|
--tone-standard-foreground: oklch(98.6% 0.004 106);
|
|
@@ -121,8 +123,10 @@
|
|
|
121
123
|
--tone-edition: oklch(50% 0.11 173);
|
|
122
124
|
--tone-edition-foreground: oklch(98.6% 0.004 106);
|
|
123
125
|
--tone-info: oklch(51% 0.12 250);
|
|
126
|
+
--tone-info-text: oklch(51% 0.12 250);
|
|
124
127
|
--tone-info-foreground: oklch(98.6% 0.004 106);
|
|
125
128
|
--tone-warning: oklch(53% 0.12 65);
|
|
129
|
+
--tone-warning-text: oklch(51% 0.12 65); /* a step under the fill: 53% is 4.44:1 as text on muted */
|
|
126
130
|
--tone-warning-foreground: oklch(98.6% 0.004 106);
|
|
127
131
|
--highlight: oklch(90% 0.05 100);
|
|
128
132
|
--highlight-foreground: oklch(24% 0.035 175);
|
|
@@ -186,9 +190,12 @@
|
|
|
186
190
|
--secondary-foreground: oklch(98.6% 0.004 106);
|
|
187
191
|
--accent: oklch(33% 0.045 175); /* above --popover: a hover row or chip inside a menu/dialog must still read */
|
|
188
192
|
--accent-foreground: oklch(98.6% 0.004 106);
|
|
189
|
-
|
|
190
|
-
--destructive
|
|
191
|
-
--
|
|
193
|
+
/* Status colours come in two: the fill carries a label (destructive sits low under white ink), and `-text` is the same colour drawn on a surface — text, borders, rings — kept light enough to read on a dark card. */
|
|
194
|
+
--destructive: oklch(57.5% 0.19 22);
|
|
195
|
+
--destructive-text: oklch(78% 0.125 22);
|
|
196
|
+
--destructive-foreground: oklch(98.6% 0.004 106);
|
|
197
|
+
--success: oklch(74% 0.13 173);
|
|
198
|
+
--success-text: oklch(74% 0.13 173);
|
|
192
199
|
--success-foreground: oklch(16% 0.03 175);
|
|
193
200
|
--tone-standard: oklch(72% 0.015 175);
|
|
194
201
|
--tone-standard-foreground: oklch(16% 0.03 175);
|
|
@@ -196,9 +203,11 @@
|
|
|
196
203
|
--tone-collectible-foreground: oklch(16% 0.03 175);
|
|
197
204
|
--tone-edition: oklch(76% 0.12 173);
|
|
198
205
|
--tone-edition-foreground: oklch(16% 0.03 175);
|
|
199
|
-
--tone-info: oklch(
|
|
206
|
+
--tone-info: oklch(76% 0.11 250);
|
|
207
|
+
--tone-info-text: oklch(76% 0.11 250);
|
|
200
208
|
--tone-info-foreground: oklch(16% 0.03 175);
|
|
201
209
|
--tone-warning: oklch(80% 0.13 75);
|
|
210
|
+
--tone-warning-text: oklch(80% 0.13 75);
|
|
202
211
|
--tone-warning-foreground: oklch(16% 0.03 175);
|
|
203
212
|
--highlight: oklch(40% 0.06 90);
|
|
204
213
|
--highlight-foreground: oklch(98.6% 0.004 106);
|
|
@@ -213,3 +222,24 @@
|
|
|
213
222
|
--sidebar-border: oklch(30% 0.03 175);
|
|
214
223
|
--sidebar-ring: oklch(85% 0.015 170);
|
|
215
224
|
}
|
|
225
|
+
|
|
226
|
+
/* Art direction for generated imagery (GradientArt; ART_TOKENS in
|
|
227
|
+
themes/_contract.ts). Out-of-focus stained glass (art-direction.md): saturated teal,
|
|
228
|
+
orange, magenta, blue and green zones melting into each other, glowing from
|
|
229
|
+
within, each zone keeping its own colour. Colour lives in imagery only, so the art is far brighter than the
|
|
230
|
+
two-tone chrome. No grain.
|
|
231
|
+
Declared on every arm so a .dark or .light island derives from its own
|
|
232
|
+
tokens rather than inheriting this scope's resolved colours. */
|
|
233
|
+
[data-sable-theme="verdant"],
|
|
234
|
+
[data-sable-theme="verdant"] .dark,
|
|
235
|
+
[data-sable-theme="verdant"] .light {
|
|
236
|
+
--theme-art-1: oklch(from var(--tone-edition) 0.76 0.15 h);
|
|
237
|
+
--theme-art-2: oklch(from var(--tone-warning) 0.76 0.16 h);
|
|
238
|
+
--theme-art-3: oklch(0.66 0.24 355);
|
|
239
|
+
--theme-art-4: oklch(from var(--tone-info) 0.62 0.17 h);
|
|
240
|
+
--theme-art-5: oklch(0.8 0.19 140);
|
|
241
|
+
--theme-art-ground: oklch(from var(--tone-collectible) 0.24 0.05 h);
|
|
242
|
+
--theme-art-warp: 0.9;
|
|
243
|
+
--theme-art-softness: 0.62;
|
|
244
|
+
--theme-art-grain: 0;
|
|
245
|
+
}
|
|
@@ -44,7 +44,7 @@ export function DangerZoneBlock({ className, framed = true, onDelete }: DangerZo
|
|
|
44
44
|
Permanently remove your profile, editions, and sales history. Collectors keep the editions they own.
|
|
45
45
|
</CardDescription>
|
|
46
46
|
</CardHeader>
|
|
47
|
-
<CardFooter className="justify-end">
|
|
47
|
+
<CardFooter actions className="justify-end">
|
|
48
48
|
<Button variant="destructive" onClick={() => setOpen(true)}>
|
|
49
49
|
Delete account
|
|
50
50
|
</Button>
|
|
@@ -66,7 +66,7 @@ export function EventCardBlock({ className, framed = true, onRespond }: EventCar
|
|
|
66
66
|
<CardFooter className="flex-wrap justify-between gap-3">
|
|
67
67
|
<AvatarGroup aria-label={`${event.total} attendees`} total={event.total} size="sm" surface={framed ? "card" : undefined} fit>
|
|
68
68
|
{event.attendees.map((initials) => (
|
|
69
|
-
<Avatar key={initials} fallback={initials} alt={initials} />
|
|
69
|
+
<Avatar variant="art" key={initials} fallback={initials} alt={initials} />
|
|
70
70
|
))}
|
|
71
71
|
</AvatarGroup>
|
|
72
72
|
<div className="flex gap-2">
|
|
@@ -52,7 +52,7 @@ export function FeedbackFormBlock({ className, framed = true, onDone }: Feedback
|
|
|
52
52
|
</ToggleGroup>
|
|
53
53
|
<Textarea placeholder="What worked, what didn’t…" aria-label="Notes" rows={3} value={notes} onChange={(e) => setNotes(e.target.value)} />
|
|
54
54
|
</CardContent>
|
|
55
|
-
<CardFooter className="justify-end">
|
|
55
|
+
<CardFooter actions className="justify-end">
|
|
56
56
|
<Button variant="ghost" size="sm" onClick={() => onDone?.(false)}>
|
|
57
57
|
Skip
|
|
58
58
|
</Button>
|
|
@@ -41,7 +41,7 @@ export function InviteTeamBlock({ className, framed = true, onSend }: InviteTeam
|
|
|
41
41
|
<CardContent className="flex flex-col gap-4">
|
|
42
42
|
<AvatarGroup aria-label={`${collaborators.length} collaborators`} size="sm" surface={framed ? "card" : undefined}>
|
|
43
43
|
{collaborators.map((initials) => (
|
|
44
|
-
<Avatar key={initials} fallback={initials} alt={initials} />
|
|
44
|
+
<Avatar variant="art" key={initials} fallback={initials} alt={initials} />
|
|
45
45
|
))}
|
|
46
46
|
</AvatarGroup>
|
|
47
47
|
<form
|
|
@@ -21,24 +21,10 @@ A now-playing card: cover, title and artist as an Entity with a Like action, a p
|
|
|
21
21
|
|
|
22
22
|
```tsx
|
|
23
23
|
import { useState } from "react"
|
|
24
|
-
import { Button, Card, CardContent, cn, Entity, Icon, Slider } from "@sableui/react"
|
|
24
|
+
import { Button, Card, CardContent, cn, Entity, GradientArt, Icon, Slider } from "@sableui/react"
|
|
25
25
|
|
|
26
26
|
const track = { title: "Low tide, high noon", artist: "Juno Avery", position: 38, elapsed: "1:24", remaining: "-2:16" }
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
* Placeholder cover painted from the palette — the theme's decorative tones
|
|
30
|
-
* over the brand, so it follows every look. Replace with the cover image.
|
|
31
|
-
*/
|
|
32
|
-
const ART_TONES = ["info", "collectible", "edition"]
|
|
33
|
-
const ART_SPOTS = [[20, 20, 70], [85, 30, 60], [60, 90, 65]]
|
|
34
|
-
const cover = [
|
|
35
|
-
...ART_SPOTS.map(
|
|
36
|
-
([x, y, reach], i) =>
|
|
37
|
-
// design-lint-disable-next-line inline-color -- relative colour derived from the theme's own --tone token, so it follows every look
|
|
38
|
-
`radial-gradient(in oklab at ${x}% ${y}%, oklch(from var(--tone-${ART_TONES[i % ART_TONES.length]}) clamp(0.5, calc(l + 0.16), 0.9) max(calc(c * 1.25), 0.1) h), transparent ${reach}%)`,
|
|
39
|
-
),
|
|
40
|
-
"var(--primary)",
|
|
41
|
-
].join(", ")
|
|
42
28
|
|
|
43
29
|
export interface MediaPlayerBlockProps {
|
|
44
30
|
className?: string
|
|
@@ -59,7 +45,8 @@ export function MediaPlayerBlock({ className, framed = true, onPlayingChange, on
|
|
|
59
45
|
card already pads it. */}
|
|
60
46
|
<Entity
|
|
61
47
|
className="p-0"
|
|
62
|
-
|
|
48
|
+
// Placeholder cover painted from the theme. Replace with the cover image.
|
|
49
|
+
leading={<GradientArt aria-hidden seed={track.title} className="size-14 rounded-md" />}
|
|
63
50
|
title={track.title}
|
|
64
51
|
subtitle={track.artist}
|
|
65
52
|
action={
|
|
@@ -9,7 +9,7 @@ An activity list: Entity rows with the actor's Avatar, who did what, and a compa
|
|
|
9
9
|
## Notes
|
|
10
10
|
|
|
11
11
|
- Rows are Card + CardList + Entity, inset from the card's padding at any density.
|
|
12
|
-
- Unread is a quiet info dot
|
|
12
|
+
- Unread is a quiet info dot before the time, so the times keep one right edge — the list stays calm when most items are unread.
|
|
13
13
|
- framed={false} drops the surface for a Popover, which already is one.
|
|
14
14
|
|
|
15
15
|
## Accessibility
|
|
@@ -40,7 +40,7 @@ export function NotificationListBlock({ className, framed = true }: Notification
|
|
|
40
40
|
{notifications.map((n) => (
|
|
41
41
|
<Entity
|
|
42
42
|
key={n.name}
|
|
43
|
-
leading={<Avatar size="sm" fallback={n.initials} alt={n.name} />}
|
|
43
|
+
leading={<Avatar variant="art" size="sm" fallback={n.initials} alt={n.name} />}
|
|
44
44
|
title={
|
|
45
45
|
<>
|
|
46
46
|
{n.name}
|
|
@@ -50,8 +50,8 @@ export function NotificationListBlock({ className, framed = true }: Notification
|
|
|
50
50
|
subtitle={n.what}
|
|
51
51
|
action={
|
|
52
52
|
<span className="flex items-center gap-1.5 text-caption text-muted-foreground">
|
|
53
|
-
{n.when}
|
|
54
53
|
{n.unread ? <StatusDot tone="info" /> : null}
|
|
54
|
+
{n.when}
|
|
55
55
|
</span>
|
|
56
56
|
}
|
|
57
57
|
/>
|
|
@@ -20,25 +20,10 @@ A product card: edge-to-edge artwork, title and maker, an edition Badge, and the
|
|
|
20
20
|
## Source
|
|
21
21
|
|
|
22
22
|
```tsx
|
|
23
|
-
import { Badge, Button, Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle, cn } from "@sableui/react"
|
|
23
|
+
import { Badge, Button, Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle, cn, GradientArt } from "@sableui/react"
|
|
24
24
|
|
|
25
25
|
const product = { title: "Untitled (gold rush)", maker: "Mara Okafor", edition: "12 of 50", price: "0.42 SOL" }
|
|
26
26
|
|
|
27
|
-
/**
|
|
28
|
-
* Placeholder artwork painted from the palette — a soft mesh of the theme's
|
|
29
|
-
* decorative tones over the brand, so it follows every look. Replace the art
|
|
30
|
-
* <div> with your product's image.
|
|
31
|
-
*/
|
|
32
|
-
const ART_TONES = ["info", "collectible", "edition", "standard"]
|
|
33
|
-
const ART_SPOTS = [[18, 22, 58], [82, 18, 55], [70, 88, 60], [12, 92, 55]]
|
|
34
|
-
const artwork = [
|
|
35
|
-
...ART_SPOTS.map(
|
|
36
|
-
([x, y, reach], i) =>
|
|
37
|
-
// design-lint-disable-next-line inline-color -- relative colour derived from the theme's own --tone token, so it follows every look
|
|
38
|
-
`radial-gradient(in oklab at ${x}% ${y}%, oklch(from var(--tone-${ART_TONES[i % ART_TONES.length]}) clamp(0.5, calc(l + 0.16), 0.9) max(calc(c * 1.25), 0.1) h), transparent ${reach}%)`,
|
|
39
|
-
),
|
|
40
|
-
"var(--primary)",
|
|
41
|
-
].join(", ")
|
|
42
27
|
|
|
43
28
|
export interface ProductCardBlockProps {
|
|
44
29
|
className?: string
|
|
@@ -51,7 +36,8 @@ export interface ProductCardBlockProps {
|
|
|
51
36
|
export function ProductCardBlock({ className, framed = true, onCollect }: ProductCardBlockProps) {
|
|
52
37
|
return (
|
|
53
38
|
<Card variant={framed ? "default" : "flat"} className={cn("w-full max-w-xs overflow-hidden", className)}>
|
|
54
|
-
|
|
39
|
+
{/* Placeholder art painted from the theme, so it follows every look. Replace with the product image. */}
|
|
40
|
+
<GradientArt aria-hidden seed={product.title} className="aspect-[4/3]" />
|
|
55
41
|
<CardHeader>
|
|
56
42
|
<CardTitle>{product.title}</CardTitle>
|
|
57
43
|
<CardDescription>{product.maker}</CardDescription>
|
|
@@ -21,7 +21,7 @@ A profile card: a cover banner, an avatar rising into it, Share and Follow, the
|
|
|
21
21
|
|
|
22
22
|
```tsx
|
|
23
23
|
import { useState } from "react"
|
|
24
|
-
import { Avatar, Button, Card, CardContent, CardDescription, CardTitle, cn, Icon } from "@sableui/react"
|
|
24
|
+
import { Avatar, Button, Card, CardContent, CardDescription, CardTitle, cn, GradientArt, Icon } from "@sableui/react"
|
|
25
25
|
|
|
26
26
|
const profile = {
|
|
27
27
|
name: "Juno Avery",
|
|
@@ -34,20 +34,6 @@ const profile = {
|
|
|
34
34
|
],
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/**
|
|
38
|
-
* Placeholder banner painted from the palette — the theme's decorative tones
|
|
39
|
-
* over the brand, so it follows every look. Replace with the person's cover image.
|
|
40
|
-
*/
|
|
41
|
-
const ART_TONES = ["info", "collectible", "edition", "standard"]
|
|
42
|
-
const ART_SPOTS = [[10, 50, 60], [50, 0, 55], [90, 60, 60], [40, 100, 50]]
|
|
43
|
-
const banner = [
|
|
44
|
-
...ART_SPOTS.map(
|
|
45
|
-
([x, y, reach], i) =>
|
|
46
|
-
// design-lint-disable-next-line inline-color -- relative colour derived from the theme's own --tone token, so it follows every look
|
|
47
|
-
`radial-gradient(in oklab at ${x}% ${y}%, oklch(from var(--tone-${ART_TONES[i % ART_TONES.length]}) clamp(0.5, calc(l + 0.16), 0.9) max(calc(c * 1.25), 0.1) h), transparent ${reach}%)`,
|
|
48
|
-
),
|
|
49
|
-
"var(--primary)",
|
|
50
|
-
].join(", ")
|
|
51
37
|
|
|
52
38
|
export interface ProfileCardBlockProps {
|
|
53
39
|
className?: string
|
|
@@ -62,12 +48,13 @@ export function ProfileCardBlock({ className, framed = true, onFollowChange, onS
|
|
|
62
48
|
const [following, setFollowing] = useState(false)
|
|
63
49
|
return (
|
|
64
50
|
<Card variant={framed ? "default" : "flat"} className={cn("w-full max-w-xs overflow-hidden", className)}>
|
|
65
|
-
|
|
51
|
+
{/* Placeholder banner painted from the theme, so it follows every look. Replace with the person's cover image. */}
|
|
52
|
+
<GradientArt aria-hidden seed={profile.handle} className="h-20" />
|
|
66
53
|
{/* pt-0: the avatar rises into the banner above, so the content
|
|
67
54
|
starts at the banner's edge rather than a full pad below it. */}
|
|
68
55
|
<CardContent className="flex flex-col gap-3 pt-0">
|
|
69
56
|
<div className="flex items-start justify-between">
|
|
70
|
-
<Avatar size="lg" fallback={profile.initials} alt={profile.name} className="-mt-6 ring-4 ring-card" />
|
|
57
|
+
<Avatar variant="art" size="lg" fallback={profile.initials} alt={profile.name} className="-mt-6 ring-4 ring-card" />
|
|
71
58
|
<div className="flex gap-1.5 pt-3">
|
|
72
59
|
<Button variant="ghost" size="icon-sm" aria-label="Share profile" onClick={onShare}>
|
|
73
60
|
<Icon name="share" />
|
|
@@ -64,7 +64,7 @@ export function ProfileFormBlock({ className, framed = true, onSave }: ProfileFo
|
|
|
64
64
|
<CardContent>
|
|
65
65
|
<Stack gap={3}>
|
|
66
66
|
<Row gap={2}>
|
|
67
|
-
<Avatar size="lg" fallback={profile.initials} alt={profile.name} />
|
|
67
|
+
<Avatar variant="art" size="lg" fallback={profile.initials} alt={profile.name} />
|
|
68
68
|
<Stack gap={0.5} align="start">
|
|
69
69
|
<Row gap={1}>
|
|
70
70
|
<Button type="button" variant="outline" size="sm">
|
|
@@ -104,7 +104,7 @@ export function ProfileFormBlock({ className, framed = true, onSave }: ProfileFo
|
|
|
104
104
|
</Grid>
|
|
105
105
|
</Stack>
|
|
106
106
|
</CardContent>
|
|
107
|
-
<CardFooter className="justify-end">
|
|
107
|
+
<CardFooter actions className="justify-end">
|
|
108
108
|
<Button type="submit">Save profile</Button>
|
|
109
109
|
</CardFooter>
|
|
110
110
|
</Form>
|
|
@@ -54,7 +54,7 @@ export function TeamMembersBlock({ className, framed = true, onInvite, onMemberA
|
|
|
54
54
|
{members.map((m) => (
|
|
55
55
|
<Entity
|
|
56
56
|
key={m.id}
|
|
57
|
-
leading={<Avatar size="sm" fallback={m.initials} alt={m.name} />}
|
|
57
|
+
leading={<Avatar variant="art" size="sm" fallback={m.initials} alt={m.name} />}
|
|
58
58
|
title={m.name}
|
|
59
59
|
subtitle={m.email}
|
|
60
60
|
action={
|
|
@@ -50,7 +50,7 @@ export function TestimonialBlock({ className }: TestimonialBlockProps) {
|
|
|
50
50
|
<blockquote className="text-balance text-heading-3 sm:text-heading-2">“{story.quote}”</blockquote>
|
|
51
51
|
</Stack>
|
|
52
52
|
<figcaption className="flex items-center gap-3">
|
|
53
|
-
<Avatar size="lg" fallback={story.initials} alt={story.name} />
|
|
53
|
+
<Avatar variant="art" size="lg" fallback={story.initials} alt={story.name} />
|
|
54
54
|
<div>
|
|
55
55
|
<p className="text-title-sm">{story.name}</p>
|
|
56
56
|
<p className="text-body-sm text-muted-foreground">{story.role}</p>
|
|
@@ -12,6 +12,7 @@ Use Avatar to represent a person, team, or account with a compact visual identit
|
|
|
12
12
|
|
|
13
13
|
### Best practices
|
|
14
14
|
|
|
15
|
+
- Do: Use `variant="art"` where people without photos sit side by side — a member list, an activity feed, a chat. Seeded by the person, each gets their own colour, so faces stay tellable apart at a glance; the neutral fallback makes everyone the same grey circle.
|
|
15
16
|
- Do: Provide meaningful alt text for profile images, or an empty alt when the adjacent text already names the person.
|
|
16
17
|
- Do: Use initials or a simple glyph fallback so missing images still communicate identity.
|
|
17
18
|
- Do: Use consistent sizes within a group; reserve lg for profile headers or major identity moments.
|
|
@@ -50,14 +51,17 @@ Use Avatar to represent a person, team, or account with a compact visual identit
|
|
|
50
51
|
| fallbackDelay | number | — | Delay before showing the fallback, in ms — avoids a flash on fast loads. |
|
|
51
52
|
| status | ReactNode | — | A presence indicator pinned to the bottom-end edge, usually <StatusDot tone="success" />. It sits outside the clipped circle and wears a ring in the surface colour (--avatar-group-ring, else the page background). Use this instead of absolutely positioning a dot over an Avatar. |
|
|
52
53
|
| size | "sm" \| "md" \| "lg" | "md" | Square footprint: 32px / 40px / 48px. |
|
|
54
|
+
| variant | "neutral" \| "art" | "neutral" | What the fallback looks like. neutral: initials on the muted surface. art: initials over GradientArt seeded by the person, so each person without a photo gets their own stable colour from the theme. |
|
|
55
|
+
| seed | string \| number | — | The art variant's seed. Defaults to the fallback text, then `alt`, so a person keeps one colour wherever they appear; pass a user id to tell apart two people who share initials. |
|
|
53
56
|
|
|
54
57
|
## Variants
|
|
55
58
|
|
|
56
59
|
- **size**: sm, md, lg
|
|
60
|
+
- **variant**: neutral, art
|
|
57
61
|
|
|
58
62
|
## Tokens used
|
|
59
63
|
|
|
60
|
-
`muted`, `muted-foreground`, `background`, `card`, `popover`
|
|
64
|
+
`muted`, `muted-foreground`, `background`, `card`, `popover`, `overlay`, `overlay-foreground`
|
|
61
65
|
|
|
62
66
|
## Accessibility
|
|
63
67
|
|
|
@@ -90,6 +94,14 @@ import { Avatar } from "@sableui/react"
|
|
|
90
94
|
<Avatar fallback="AM" alt="Alex Morgan" />
|
|
91
95
|
```
|
|
92
96
|
|
|
97
|
+
### Art fallback
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { Avatar } from "@sableui/react"
|
|
101
|
+
|
|
102
|
+
<Avatar variant="art" fallback="AM" alt="Alex Morgan" />
|
|
103
|
+
```
|
|
104
|
+
|
|
93
105
|
### Sizes
|
|
94
106
|
|
|
95
107
|
```tsx
|