@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.
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/goal-card/goal-card.css +592 -0
- package/dist/goal-card/goal-card.d.ts +92 -0
- package/dist/goal-card/goal-card.d.ts.map +1 -0
- package/dist/goal-card/goal-card.js +66 -0
- package/dist/goal-card/goal-card.js.map +1 -0
- package/dist/goal-card/index.d.ts +3 -0
- package/dist/goal-card/index.d.ts.map +1 -0
- package/dist/goal-card/index.js +2 -0
- package/dist/goal-card/index.js.map +1 -0
- package/dist/goal-categories/goal-categories.css +51 -0
- package/dist/goal-categories/goal-categories.d.ts +55 -0
- package/dist/goal-categories/goal-categories.d.ts.map +1 -0
- package/dist/goal-categories/goal-categories.js +103 -0
- package/dist/goal-categories/goal-categories.js.map +1 -0
- package/dist/goal-categories/index.d.ts +3 -0
- package/dist/goal-categories/index.d.ts.map +1 -0
- package/dist/goal-categories/index.js +2 -0
- package/dist/goal-categories/index.js.map +1 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.css +10 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.d.ts +11 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.d.ts.map +1 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.js +34 -0
- package/dist/goal-tab-trigger/goal-tab-trigger.js.map +1 -0
- package/dist/goal-tab-trigger/index.d.ts +3 -0
- package/dist/goal-tab-trigger/index.d.ts.map +1 -0
- package/dist/goal-tab-trigger/index.js +2 -0
- package/dist/goal-tab-trigger/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- 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
|
+
}
|