@manakind/manuia-ui 0.2.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.
Files changed (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -0
  3. package/dist/goal-card/goal-card.css +592 -0
  4. package/dist/goal-card/goal-card.d.ts +92 -0
  5. package/dist/goal-card/goal-card.d.ts.map +1 -0
  6. package/dist/goal-card/goal-card.js +66 -0
  7. package/dist/goal-card/goal-card.js.map +1 -0
  8. package/dist/goal-card/index.d.ts +3 -0
  9. package/dist/goal-card/index.d.ts.map +1 -0
  10. package/dist/goal-card/index.js +2 -0
  11. package/dist/goal-card/index.js.map +1 -0
  12. package/dist/goal-categories/goal-categories.css +51 -0
  13. package/dist/goal-categories/goal-categories.d.ts +55 -0
  14. package/dist/goal-categories/goal-categories.d.ts.map +1 -0
  15. package/dist/goal-categories/goal-categories.js +103 -0
  16. package/dist/goal-categories/goal-categories.js.map +1 -0
  17. package/dist/goal-categories/index.d.ts +3 -0
  18. package/dist/goal-categories/index.d.ts.map +1 -0
  19. package/dist/goal-categories/index.js +2 -0
  20. package/dist/goal-categories/index.js.map +1 -0
  21. package/dist/goal-tab-trigger/goal-tab-trigger.css +10 -0
  22. package/dist/goal-tab-trigger/goal-tab-trigger.d.ts +11 -0
  23. package/dist/goal-tab-trigger/goal-tab-trigger.d.ts.map +1 -0
  24. package/dist/goal-tab-trigger/goal-tab-trigger.js +34 -0
  25. package/dist/goal-tab-trigger/goal-tab-trigger.js.map +1 -0
  26. package/dist/goal-tab-trigger/index.d.ts +3 -0
  27. package/dist/goal-tab-trigger/index.d.ts.map +1 -0
  28. package/dist/goal-tab-trigger/index.js +2 -0
  29. package/dist/goal-tab-trigger/index.js.map +1 -0
  30. package/dist/index.d.ts +15 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +15 -0
  33. package/dist/index.js.map +1 -0
  34. package/package.json +75 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Humanity Plus Solutions
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # `@manakind/manuia-ui`
2
+
3
+ The Manuia-flavored surface of the [Manakind](https://github.com/Humanity-Plus/Manakind)
4
+ design system. Today it re-exports the shared base components from
5
+ `@manakind/ui-core`; Manuia-specific compositions (cards, flows, layouts unique
6
+ to Manuia) will live alongside this barrel as they are built.
7
+
8
+ Pair it with `@manakind/manuia-tokens`, whose `theme.css` resolves the `--ds-*`
9
+ contract into Manuia's visual style.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @manakind/manuia-ui @manakind/manuia-tokens react react-dom
15
+ ```
16
+
17
+ `react` and `react-dom` (`^19`) are peer dependencies. `@manakind/ui-core` and
18
+ `@manakind/manuia-tokens` are pulled in as exact-pinned dependencies and ship in
19
+ lockstep — upgrade all `@manakind/*` packages together.
20
+
21
+ ## Usage
22
+
23
+ ```tsx
24
+ import '@manakind/manuia-tokens/css/primitives';
25
+ import '@manakind/manuia-tokens/css/semantic';
26
+ import '@manakind/manuia-tokens/css/components';
27
+ import '@manakind/manuia-tokens/css/theme';
28
+
29
+ // Dark theme. Emitted under `:root.dark, :root[data-theme='dark']`, so these
30
+ // are inert until you set the class or attribute — import them even if dark
31
+ // ships later, or switching it on silently does nothing. All three are needed:
32
+ // `manuia-ui` reads --semantic-* and --component-* directly, not only --ds-*.
33
+ import '@manakind/manuia-tokens/css/semantic-dark';
34
+ import '@manakind/manuia-tokens/css/components-dark';
35
+ import '@manakind/manuia-tokens/css/theme-dark';
36
+ import { Button } from '@manakind/manuia-ui';
37
+
38
+ export function Continue() {
39
+ return <Button>Continue</Button>;
40
+ }
41
+ ```
42
+
43
+ ## License
44
+
45
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,592 @@
1
+ @layer components {
2
+ .mui-goal-card {
3
+ position: relative;
4
+ /*
5
+ * Deliberately no `height: 100%`. As a grid item that percentage resolves
6
+ * against a row the item's own content is sizing — circular, and the
7
+ * browser settles it by leaving the card taller than its row, so cards
8
+ * overlapped the one below. Grid already stretches items to equal height
9
+ * via the default `align-items: stretch`, so the declaration bought
10
+ * nothing and cost the layout.
11
+ */
12
+ overflow: hidden;
13
+ /*
14
+ * Named so the query below reads against the card and not some ancestor a
15
+ * product might later make a container. Safe here because the card's width
16
+ * is set by its grid track, never by its own content — inline-size
17
+ * containment would otherwise cut the card off from what sizes it.
18
+ */
19
+ container: mui-goal-card / inline-size;
20
+ border-radius: var(--component-goal-card-radius);
21
+ border-color: var(--mui-tier-accent, var(--ds-card-border));
22
+ padding: var(--mui-tier-pad, var(--ds-card-padding));
23
+ box-shadow: var(--mui-tier-shadow, var(--semantic-elevation-resting));
24
+
25
+ /*
26
+ * Matches the product's `backdrop-blur-sm`. It only reads because the
27
+ * wash gradient below ends in a partly transparent card colour — against
28
+ * an opaque fill there would be nothing to blur.
29
+ */
30
+ backdrop-filter: blur(4px);
31
+
32
+ &:hover {
33
+ box-shadow: var(--mui-tier-shadow-hover, var(--semantic-elevation-raised));
34
+ }
35
+
36
+ &[data-interactive] {
37
+ transition:
38
+ box-shadow 300ms cubic-bezier(0, 0, 0.2, 1),
39
+ border-color 300ms cubic-bezier(0, 0, 0.2, 1),
40
+ transform 300ms cubic-bezier(0, 0, 0.2, 1);
41
+
42
+ &:hover {
43
+ transform: var(--mui-tier-hover-transform, none);
44
+ }
45
+ }
46
+
47
+ /*
48
+ * The tier wash. Only the decorated tiers define `--mui-tier-wash`, so the
49
+ * fallback resolves to no image on Monthly and Weekly.
50
+ */
51
+ background-image: linear-gradient(
52
+ to bottom right,
53
+ color-mix(in srgb, var(--mui-tier-wash, transparent) 70%, transparent),
54
+ color-mix(in srgb, var(--ds-card-bg) 60%, transparent)
55
+ );
56
+
57
+ /*
58
+ * Header band. Negative margins pull it out to the card edge so its
59
+ * background and top rule meet the rounded corner, then the padding is
60
+ * restored inside. `overflow: hidden` on the card clips it to the radius.
61
+ */
62
+ & .mui-goal-card__header {
63
+ margin: calc(-1 * var(--mui-tier-pad, var(--ds-card-padding)))
64
+ calc(-1 * var(--mui-tier-pad, var(--ds-card-padding))) 0;
65
+ padding: var(--mui-tier-pad, var(--ds-card-padding));
66
+ padding-block-end: var(--mui-tier-header-gap, var(--ds-card-gap));
67
+ background-image: linear-gradient(
68
+ to right,
69
+ color-mix(in srgb, var(--mui-tier-badge-bg, transparent) 30%, transparent),
70
+ color-mix(in srgb, var(--mui-tier-wash, transparent) 20%, transparent),
71
+ transparent
72
+ );
73
+ }
74
+
75
+ /* Hairline closing the header, with a diamond on the top tier only. */
76
+ & .mui-goal-card__divider {
77
+ display: none;
78
+ align-items: center;
79
+ gap: var(--semantic-space-gap);
80
+
81
+ &::before,
82
+ &::after {
83
+ content: "";
84
+ flex: 1;
85
+ height: 1px;
86
+ background-color: color-mix(in srgb, var(--mui-tier-accent) 20%, transparent);
87
+ }
88
+ }
89
+
90
+ /* Inset frame, drawn only where a tier sets an inset. */
91
+ & .mui-goal-card__ornament {
92
+ display: none;
93
+ position: absolute;
94
+ inset: var(--mui-tier-ornament-inset);
95
+ border: 1px solid color-mix(in srgb, var(--mui-tier-accent) 25%, transparent);
96
+ border-radius: var(--semantic-radius-surface);
97
+ pointer-events: none;
98
+ }
99
+
100
+ & .mui-goal-card__corners {
101
+ display: none;
102
+ position: absolute;
103
+ inset: var(--mui-tier-corner-inset);
104
+ pointer-events: none;
105
+
106
+ & > span {
107
+ position: absolute;
108
+ }
109
+
110
+ & > span:nth-child(1) {
111
+ top: 0;
112
+ left: 0;
113
+ }
114
+
115
+ & > span:nth-child(2) {
116
+ top: 0;
117
+ right: 0;
118
+ }
119
+
120
+ & > span:nth-child(3) {
121
+ bottom: 0;
122
+ left: 0;
123
+ }
124
+
125
+ & > span:nth-child(4) {
126
+ bottom: 0;
127
+ right: 0;
128
+ }
129
+ }
130
+
131
+ &.mui-goal-card--legendary,
132
+ &.mui-goal-card--epic {
133
+ & .mui-goal-card__ornament,
134
+ & .mui-goal-card__corners {
135
+ display: block;
136
+ }
137
+ }
138
+
139
+ /* 5 Year: L-shaped corner brackets. */
140
+ &.mui-goal-card--legendary .mui-goal-card__corners > span {
141
+ &::before,
142
+ &::after {
143
+ content: "";
144
+ position: absolute;
145
+ background-color: color-mix(in srgb, var(--mui-tier-accent) 30%, transparent);
146
+ }
147
+
148
+ &::before {
149
+ width: 16px;
150
+ height: 1.5px;
151
+ }
152
+
153
+ &::after {
154
+ width: 1.5px;
155
+ height: 16px;
156
+ }
157
+ }
158
+
159
+ &.mui-goal-card--legendary .mui-goal-card__corners > span:nth-child(1) {
160
+ &::before,
161
+ &::after {
162
+ top: 0;
163
+ left: 0;
164
+ }
165
+ }
166
+
167
+ &.mui-goal-card--legendary .mui-goal-card__corners > span:nth-child(2) {
168
+ &::before,
169
+ &::after {
170
+ top: 0;
171
+ right: 0;
172
+ }
173
+ }
174
+
175
+ &.mui-goal-card--legendary .mui-goal-card__corners > span:nth-child(3) {
176
+ &::before,
177
+ &::after {
178
+ bottom: 0;
179
+ left: 0;
180
+ }
181
+ }
182
+
183
+ &.mui-goal-card--legendary .mui-goal-card__corners > span:nth-child(4) {
184
+ &::before,
185
+ &::after {
186
+ bottom: 0;
187
+ right: 0;
188
+ }
189
+ }
190
+
191
+ /* 1 Year: corner dots. */
192
+ &.mui-goal-card--epic .mui-goal-card__corners > span {
193
+ width: 4px;
194
+ height: 4px;
195
+ border-radius: var(--semantic-radius-pill);
196
+ background-color: color-mix(in srgb, var(--mui-tier-accent) 30%, transparent);
197
+ }
198
+
199
+ &.mui-goal-card--legendary .mui-goal-card__divider,
200
+ &.mui-goal-card--epic .mui-goal-card__divider {
201
+ display: flex;
202
+ }
203
+
204
+ &.mui-goal-card--legendary .mui-goal-card__divider {
205
+ position: relative;
206
+
207
+ &::before,
208
+ &::after {
209
+ background-color: color-mix(in srgb, var(--mui-tier-accent) 20%, transparent);
210
+ }
211
+ }
212
+
213
+ &.mui-goal-card--epic .mui-goal-card__divider {
214
+ &::before,
215
+ &::after {
216
+ background-color: color-mix(in srgb, var(--mui-tier-accent) 15%, transparent);
217
+ }
218
+ }
219
+
220
+ /*
221
+ * Emblem and timeframe stay together on the left; status sits alone on the
222
+ * right. `space-between` across all three spread them evenly, which read
223
+ * as three unrelated chips rather than one cluster plus a status.
224
+ */
225
+ /*
226
+ * The container query below keeps this on one line in the normal case.
227
+ * `flex-wrap` stays as the fallback for the two cases it cannot cover:
228
+ * a card given no `statusIcon` (the badge keeps its text rather than
229
+ * collapsing to a bare colour chip — see the query), and a browser
230
+ * without container queries. Wrapping to a second line is a mild,
231
+ * legible degradation; the card is `overflow: hidden`, so without this
232
+ * the failure mode is a silently clipped status instead.
233
+ */
234
+ & .mui-goal-card__meta {
235
+ display: flex;
236
+ align-items: center;
237
+ justify-content: space-between;
238
+ flex-wrap: wrap;
239
+ gap: var(--semantic-space-gap) var(--semantic-space-gap-relaxed);
240
+ }
241
+
242
+ & .mui-goal-card__meta-start {
243
+ display: inline-flex;
244
+ align-items: center;
245
+ gap: var(--semantic-space-gap);
246
+ /*
247
+ * No `min-width: 0`. That let this group shrink past its content, and
248
+ * because everything inside is `nowrap` the overflow showed up as a
249
+ * chip running straight through the badge rather than as a reflow.
250
+ */
251
+ min-width: auto;
252
+ }
253
+
254
+ & .mui-goal-card__emblem {
255
+ display: inline-flex;
256
+ align-items: center;
257
+ justify-content: center;
258
+ flex-shrink: 0;
259
+ width: var(--mui-tier-emblem-size);
260
+ height: var(--mui-tier-emblem-size);
261
+ border-radius: var(--semantic-radius-pill);
262
+ color: var(--mui-tier-emblem-icon);
263
+ /*
264
+ * Rare has no mid stop; its fallback is the sRGB midpoint, which draws
265
+ * exactly the two-stop gradient. `in srgb` pins the space so that a
266
+ * color-mix() stop doesn't switch the gradient to oklab.
267
+ */
268
+ background-image: linear-gradient(
269
+ to bottom right in srgb,
270
+ var(--mui-tier-emblem-from),
271
+ var(
272
+ --mui-tier-emblem-via,
273
+ color-mix(in srgb, var(--mui-tier-emblem-from), var(--mui-tier-emblem-to))
274
+ ),
275
+ var(--mui-tier-emblem-to)
276
+ );
277
+ /* Composed in a custom property: declaration-strict-value rejects any comma list on box-shadow. */
278
+ --mui-tier-emblem-box-shadow:
279
+ 0 0 0 var(--mui-tier-emblem-ring-width)
280
+ color-mix(
281
+ in srgb,
282
+ var(--mui-tier-emblem-ring-color) var(--mui-tier-emblem-ring-alpha),
283
+ transparent
284
+ ),
285
+ var(--mui-tier-emblem-shadow, 0 0 transparent);
286
+ box-shadow: var(--mui-tier-emblem-box-shadow);
287
+ }
288
+
289
+ /*
290
+ * Plain labelled metadata, not a component. The pill fill was one of four
291
+ * on a single card, and the timeframe never needed the emphasis — the
292
+ * emblem beside it already carries the tier.
293
+ *
294
+ * `--mui-tier-badge-text` is still read, so the higher tiers keep tinting
295
+ * the timeframe; only the fill and its padding are gone.
296
+ */
297
+ & .mui-goal-card__type {
298
+ display: inline-flex;
299
+ align-items: center;
300
+ gap: var(--component-goal-card-chip-gap);
301
+ color: var(--mui-tier-badge-text, var(--semantic-color-text-muted));
302
+ font-size: var(--component-goal-card-chip-font-size);
303
+ font-weight: var(--primitives-font-weight-semibold);
304
+ white-space: nowrap;
305
+ }
306
+
307
+ & .mui-goal-card__categories {
308
+ margin-block-start: var(--semantic-space-gap);
309
+ }
310
+
311
+ /*
312
+ * Collapse the status badge to its glyph when the meta row runs out of
313
+ * room.
314
+ *
315
+ * A size query measures the container's CONTENT box, not its border box,
316
+ * so the card's ~50px of padding is already excluded here — the number
317
+ * below is how much room the row actually gets, not how wide the card is.
318
+ * Getting that backwards is what made the first attempt fire at every
319
+ * width on the goals page.
320
+ *
321
+ * Measured across all five tiers with the label forced visible: the
322
+ * widest pairing is "Quarterly" against "Not Started" at 237px, and both
323
+ * `GoalType` and `GoalStatus` are closed unions, so nothing longer can
324
+ * arrive. 16rem (256px) collapses before that with 19px to spare.
325
+ *
326
+ * The number has moved twice. It was 16rem against 243px until a `ui-core`
327
+ * Badge change added ~12px to every status, leaving 1px of headroom — a
328
+ * threshold that had silently stopped working while every check stayed
329
+ * green — so it went to 17rem against 255px. The plain status badge (#177)
330
+ * then dropped the chip's padding and border, bringing the row to 237px.
331
+ * Re-measure this after any Badge geometry change, and keep the margin
332
+ * wide enough that normal drift cannot close it.
333
+ *
334
+ * The `Statuses` play function is the backstop that makes the margin
335
+ * safe to reason about rather than something to over-pad: it fails if the
336
+ * row ever overflows at a width where the label is meant to show.
337
+ *
338
+ * `rem` rather than `px` on purpose: the query then moves with the user's
339
+ * root font size, so someone browsing at 150% collapses the badge at the
340
+ * width where their text actually stops fitting.
341
+ *
342
+ * This mirrors `.ds-badge--compact` in `ui-core` rather than reusing it,
343
+ * because a container query cannot add a class. The badge is `plain`, so
344
+ * there is no chip to square up: only the label goes. The badge's
345
+ * `aria-label` means nothing is lost on the way down — only the visible
346
+ * text changes, and the accessible name is identical at both widths.
347
+ *
348
+ * `:has(.ds-icon)` is load-bearing, not defensive tidiness. `statusIcon`
349
+ * is optional, and hiding the label unconditionally would leave a card
350
+ * that passed no glyph showing a bare colour chip — colour as the only
351
+ * signal, which is the exact thing `BadgeProps`' compact union refuses to
352
+ * let a consumer write by hand. The CSS path has to refuse it too. Such a
353
+ * card keeps its text and wraps instead.
354
+ */
355
+ @container mui-goal-card (max-width: 16rem) {
356
+ & .mui-goal-card__meta .ds-badge:has(.ds-icon) .ds-badge__label {
357
+ display: none;
358
+ }
359
+ }
360
+
361
+ /*
362
+ * A heading needn't differ in colour from the body; size and grouping
363
+ * mark it. Set through the contract key `ui-core`'s title reads, so no
364
+ * specificity contest with `.ds-card__title` (tokens.md).
365
+ */
366
+ & .mui-goal-card__title {
367
+ --ds-card-text: var(--ds-card-text-muted);
368
+ }
369
+
370
+ & .mui-goal-card__description {
371
+ margin: 0;
372
+ color: var(--semantic-color-text-muted);
373
+ font-size: var(--primitives-font-size-sm);
374
+ line-height: var(--primitives-line-height-relaxed);
375
+ }
376
+
377
+ & .mui-goal-card__milestones {
378
+ display: flex;
379
+ flex-direction: column;
380
+ gap: var(--semantic-space-gap);
381
+ }
382
+
383
+ & .mui-goal-card__milestone-list {
384
+ display: flex;
385
+ flex-direction: column;
386
+ gap: var(--semantic-space-gap-tight);
387
+ margin: 0;
388
+ padding: 0;
389
+ list-style: none;
390
+ }
391
+
392
+ & .mui-goal-card__milestone {
393
+ display: flex;
394
+ align-items: center;
395
+ gap: var(--semantic-space-gap);
396
+ font-size: var(--primitives-font-size-xs);
397
+ color: var(--semantic-color-text-default);
398
+ }
399
+
400
+ & .mui-goal-card__milestone-marker {
401
+ display: inline-flex;
402
+ align-items: center;
403
+ justify-content: center;
404
+ flex-shrink: 0;
405
+ width: var(--ds-icon-size-sm);
406
+ height: var(--ds-icon-size-sm);
407
+ border: 1px solid var(--semantic-color-border-control);
408
+ border-radius: var(--semantic-radius-interactive);
409
+ color: var(--semantic-color-text-inverse);
410
+ }
411
+
412
+ & .mui-goal-card__milestone--done {
413
+ color: var(--semantic-color-text-muted);
414
+ text-decoration: line-through;
415
+
416
+ & .mui-goal-card__milestone-marker {
417
+ background-color: var(--semantic-color-action-primary);
418
+ border-color: var(--semantic-color-action-primary);
419
+ }
420
+ }
421
+
422
+ & .mui-goal-card__target {
423
+ margin: 0;
424
+ color: var(--semantic-color-text-muted);
425
+ font-size: var(--primitives-font-size-xs);
426
+ }
427
+
428
+ /*
429
+ * Mirrors the product's `text-small-caps` utility, which labels both the
430
+ * milestone summary and the target date.
431
+ */
432
+ & .mui-goal-card__target-label,
433
+ & .mui-goal-card__milestones-label {
434
+ font-weight: var(--primitives-font-weight-semibold);
435
+ font-size: var(--primitives-font-size-xs);
436
+ letter-spacing: 0.15em;
437
+ line-height: var(--primitives-line-height-tight);
438
+ text-transform: uppercase;
439
+ }
440
+
441
+ /*
442
+ * Prestige tiers.
443
+ *
444
+ * Each modifier only *sets* custom properties; the shared rules above and
445
+ * below consume them. That keeps one copy of the decoration logic instead
446
+ * of five near-identical blocks, and makes a tier a list of values.
447
+ *
448
+ * Values transcribed from GOAL_LIST_PRESTIGE / GoalPrestigeDecor in the
449
+ * product. Alpha steps use color-mix against a solid token rather than
450
+ * adding an alpha variant to the palette for every stop.
451
+ */
452
+ &.mui-goal-card--legendary {
453
+ --mui-tier-pad: var(--component-goal-card-legendary-padding);
454
+ --mui-tier-header-gap: var(--component-goal-card-legendary-header-gap);
455
+ --mui-tier-accent: var(--component-goal-card-legendary-accent);
456
+ --mui-tier-wash: var(--component-goal-card-legendary-wash);
457
+ --mui-tier-badge-bg: var(--component-goal-card-legendary-badge-bg);
458
+ --mui-tier-badge-text: var(--component-goal-card-legendary-badge-text);
459
+ --mui-tier-emblem-from: var(--component-goal-card-legendary-emblem-from);
460
+ --mui-tier-emblem-via: var(--component-goal-card-legendary-emblem-via);
461
+ --mui-tier-emblem-to: var(--component-goal-card-legendary-emblem-to);
462
+ --mui-tier-emblem-icon: var(--component-goal-card-legendary-emblem-icon);
463
+ --mui-tier-emblem-size: var(--component-goal-card-legendary-emblem-size);
464
+ --mui-tier-emblem-ring-color: var(--mui-tier-accent);
465
+ --mui-tier-emblem-ring-width: 2px;
466
+ --mui-tier-emblem-ring-alpha: 50%;
467
+ --mui-tier-emblem-shadow: var(--component-goal-card-legendary-emblem-shadow);
468
+ --mui-tier-hover-transform: scale(1.02);
469
+ --mui-tier-shadow: var(--component-goal-card-legendary-shadow);
470
+ --mui-tier-shadow-hover: var(--component-goal-card-legendary-shadow-hover);
471
+ --mui-tier-ornament-inset: var(--component-goal-card-legendary-ornament-inset);
472
+ --mui-tier-corner-inset: var(--component-goal-card-legendary-corner-inset);
473
+
474
+ border-width: 2px;
475
+
476
+ & .mui-goal-card__header {
477
+ border-top: 4px solid var(--mui-tier-accent);
478
+ }
479
+
480
+ & .mui-goal-card__title {
481
+ font-size: var(--primitives-font-size-2xl);
482
+ font-weight: var(--primitives-font-weight-regular);
483
+ }
484
+ }
485
+
486
+ &.mui-goal-card--epic {
487
+ --mui-tier-pad: var(--component-goal-card-epic-padding);
488
+ --mui-tier-header-gap: var(--component-goal-card-epic-header-gap);
489
+ --mui-tier-accent: var(--component-goal-card-epic-accent);
490
+ --mui-tier-wash: var(--component-goal-card-epic-wash);
491
+ --mui-tier-badge-bg: var(--component-goal-card-epic-badge-bg);
492
+ --mui-tier-badge-text: var(--component-goal-card-epic-badge-text);
493
+ --mui-tier-emblem-from: var(--component-goal-card-epic-emblem-from);
494
+ --mui-tier-emblem-via: var(--component-goal-card-epic-emblem-via);
495
+ --mui-tier-emblem-to: var(--component-goal-card-epic-emblem-to);
496
+ --mui-tier-emblem-icon: var(--component-goal-card-epic-emblem-icon);
497
+ --mui-tier-emblem-size: var(--component-goal-card-epic-emblem-size);
498
+ --mui-tier-emblem-ring-color: var(--mui-tier-accent);
499
+ --mui-tier-emblem-ring-width: 1px;
500
+ --mui-tier-emblem-ring-alpha: 40%;
501
+ --mui-tier-emblem-shadow: var(--component-goal-card-epic-emblem-shadow);
502
+ --mui-tier-hover-transform: scale(1.015);
503
+ --mui-tier-shadow: var(--component-goal-card-epic-shadow);
504
+ --mui-tier-shadow-hover: var(--component-goal-card-epic-shadow-hover);
505
+ --mui-tier-ornament-inset: var(--component-goal-card-epic-ornament-inset);
506
+ --mui-tier-corner-inset: var(--component-goal-card-epic-corner-inset);
507
+
508
+ border-width: 2px;
509
+
510
+ & .mui-goal-card__header {
511
+ border-top: 3px solid var(--mui-tier-accent);
512
+ }
513
+
514
+ & .mui-goal-card__title {
515
+ font-size: var(--primitives-font-size-xl);
516
+ font-weight: var(--primitives-font-weight-regular);
517
+ }
518
+ }
519
+
520
+ &.mui-goal-card--rare {
521
+ --mui-tier-pad: var(--component-goal-card-rare-padding);
522
+ --mui-tier-header-gap: var(--component-goal-card-rare-header-gap);
523
+ --mui-tier-accent: var(--component-goal-card-rare-accent);
524
+ --mui-tier-wash: var(--component-goal-card-rare-wash);
525
+ --mui-tier-badge-bg: var(--component-goal-card-rare-badge-bg);
526
+ --mui-tier-badge-text: var(--component-goal-card-rare-badge-text);
527
+ --mui-tier-emblem-from: var(--component-goal-card-rare-emblem-from);
528
+ --mui-tier-emblem-to: var(--component-goal-card-rare-emblem-to);
529
+ --mui-tier-emblem-icon: var(--component-goal-card-rare-emblem-icon);
530
+ --mui-tier-emblem-size: var(--component-goal-card-rare-emblem-size);
531
+ --mui-tier-emblem-ring-color: var(--component-goal-card-rare-emblem-ring-color);
532
+ --mui-tier-emblem-ring-width: 1px;
533
+ --mui-tier-emblem-ring-alpha: 40%;
534
+ --mui-tier-hover-transform: translateY(-2px);
535
+ --mui-tier-shadow: var(--component-goal-card-rare-shadow);
536
+ --mui-tier-shadow-hover: var(--component-goal-card-rare-shadow-hover);
537
+
538
+ border-width: 1.5px;
539
+
540
+ /* Rare and below get a left rule rather than a top band. */
541
+ & .mui-goal-card__header {
542
+ border-inline-start: 3px solid var(--mui-tier-accent);
543
+ }
544
+
545
+ & .mui-goal-card__title {
546
+ font-size: var(--primitives-font-size-lg);
547
+ font-weight: var(--primitives-font-weight-regular);
548
+ }
549
+ }
550
+
551
+ &.mui-goal-card--common {
552
+ --mui-tier-pad: var(--component-goal-card-common-padding);
553
+ --mui-tier-header-gap: var(--component-goal-card-common-header-gap);
554
+ --mui-tier-accent: var(--component-goal-card-common-accent);
555
+ --mui-tier-badge-bg: var(--component-goal-card-common-badge-bg);
556
+ --mui-tier-badge-text: var(--component-goal-card-common-badge-text);
557
+ --mui-tier-shadow: var(--component-goal-card-common-shadow);
558
+ --mui-tier-shadow-hover: var(--component-goal-card-common-shadow-hover);
559
+
560
+ & .mui-goal-card__header {
561
+ border-inline-start: 2px solid var(--mui-tier-accent);
562
+ }
563
+
564
+ & .mui-goal-card__title {
565
+ font-size: var(--primitives-font-size-md);
566
+ font-weight: var(--primitives-font-weight-regular);
567
+ }
568
+ }
569
+
570
+ &.mui-goal-card--plain {
571
+ --mui-tier-pad: var(--component-goal-card-plain-padding);
572
+ --mui-tier-header-gap: var(--component-goal-card-plain-header-gap);
573
+ --mui-tier-shadow: var(--component-goal-card-plain-shadow);
574
+ --mui-tier-shadow-hover: var(--component-goal-card-plain-shadow-hover);
575
+
576
+ & .mui-goal-card__title {
577
+ font-size: var(--primitives-font-size-md);
578
+ font-weight: var(--primitives-font-weight-regular);
579
+ }
580
+ }
581
+ }
582
+ }
583
+
584
+ @media (prefers-reduced-motion: reduce) {
585
+ .mui-goal-card[data-interactive] {
586
+ transition: box-shadow 300ms cubic-bezier(0, 0, 0.2, 1);
587
+
588
+ &:hover {
589
+ transform: none;
590
+ }
591
+ }
592
+ }