@sproutsocial/seeds-react-narrative-kit 0.1.0 → 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/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +9 -0
- package/dist/esm/index.js +296 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/eyebrow-token.css +37 -0
- package/dist/index.d.mts +205 -1
- package/dist/index.d.ts +205 -1
- package/dist/index.js +303 -3
- package/dist/index.js.map +1 -1
- package/dist/metric-highlight.css +178 -0
- package/dist/narrative-container.css +1 -1
- package/dist/narrative-headline.css +125 -0
- package/dist/pull-quote.css +89 -0
- package/package.json +9 -3
- package/src/EyebrowToken/EyebrowToken.stories.tsx +25 -0
- package/src/EyebrowToken/EyebrowToken.tsx +29 -0
- package/src/EyebrowToken/EyebrowTokenTypes.ts +7 -0
- package/src/EyebrowToken/__tests__/EyebrowToken.test.tsx +35 -0
- package/src/EyebrowToken/index.ts +2 -0
- package/src/MetricHighlight/MetricHighlight.stories.tsx +87 -0
- package/src/MetricHighlight/MetricHighlight.tsx +196 -0
- package/src/MetricHighlight/MetricHighlightTypes.ts +61 -0
- package/src/MetricHighlight/__tests__/MetricHighlight.test.tsx +151 -0
- package/src/MetricHighlight/index.ts +5 -0
- package/src/NarrativeContainer/NarrativeContainer.stories.tsx +3 -3
- package/src/NarrativeContainer/NarrativeContainer.tsx +1 -23
- package/src/NarrativeHeadline/NarrativeHeadline.stories.tsx +68 -0
- package/src/NarrativeHeadline/NarrativeHeadline.tsx +36 -0
- package/src/NarrativeHeadline/NarrativeHeadlineHighlight.tsx +31 -0
- package/src/NarrativeHeadline/NarrativeHeadlineRoll.tsx +100 -0
- package/src/NarrativeHeadline/NarrativeHeadlineTypes.ts +54 -0
- package/src/NarrativeHeadline/__tests__/NarrativeHeadline.test.tsx +138 -0
- package/src/NarrativeHeadline/index.ts +9 -0
- package/src/Playground/Playground.stories.tsx +113 -0
- package/src/PullQuote/PullQuote.stories.tsx +43 -0
- package/src/PullQuote/PullQuote.tsx +42 -0
- package/src/PullQuote/PullQuoteTypes.ts +19 -0
- package/src/PullQuote/__tests__/PullQuote.test.tsx +48 -0
- package/src/PullQuote/index.ts +2 -0
- package/src/_internal/cn.ts +22 -0
- package/src/eyebrow-token.css +37 -0
- package/src/index.ts +20 -0
- package/src/metric-highlight.css +178 -0
- package/src/narrative-container.css +1 -1
- package/src/narrative-headline.css +125 -0
- package/src/pull-quote.css +89 -0
package/dist/index.js
CHANGED
|
@@ -30,13 +30,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
|
|
33
|
+
EyebrowToken: () => EyebrowToken_default,
|
|
34
|
+
MetricHighlight: () => MetricHighlight_default,
|
|
35
|
+
NarrativeContainer: () => NarrativeContainer_default,
|
|
36
|
+
NarrativeHeadline: () => NarrativeHeadline_default,
|
|
37
|
+
NarrativeHeadlineHighlight: () => NarrativeHeadlineHighlight_default,
|
|
38
|
+
NarrativeHeadlineRoll: () => NarrativeHeadlineRoll_default,
|
|
39
|
+
PullQuote: () => PullQuote_default
|
|
34
40
|
});
|
|
35
41
|
module.exports = __toCommonJS(index_exports);
|
|
36
42
|
|
|
37
43
|
// src/NarrativeContainer/NarrativeContainer.tsx
|
|
38
44
|
var React = __toESM(require("react"));
|
|
39
|
-
|
|
45
|
+
|
|
46
|
+
// src/_internal/cn.ts
|
|
40
47
|
function cn(...inputs) {
|
|
41
48
|
const classes = [];
|
|
42
49
|
for (const input of inputs) {
|
|
@@ -53,6 +60,9 @@ function cn(...inputs) {
|
|
|
53
60
|
}
|
|
54
61
|
return classes.join(" ");
|
|
55
62
|
}
|
|
63
|
+
|
|
64
|
+
// src/NarrativeContainer/NarrativeContainer.tsx
|
|
65
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
56
66
|
var NarrativeContainer = React.forwardRef(({ children, accent = true, className, ...props }, ref) => {
|
|
57
67
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
58
68
|
"div",
|
|
@@ -70,8 +80,298 @@ var NarrativeContainer = React.forwardRef(({ children, accent = true, className,
|
|
|
70
80
|
});
|
|
71
81
|
NarrativeContainer.displayName = "NarrativeContainer";
|
|
72
82
|
var NarrativeContainer_default = NarrativeContainer;
|
|
83
|
+
|
|
84
|
+
// src/NarrativeHeadline/NarrativeHeadline.tsx
|
|
85
|
+
var React2 = __toESM(require("react"));
|
|
86
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
87
|
+
var NarrativeHeadline = React2.forwardRef(({ children, headingLevel = 2, className, ...props }, ref) => {
|
|
88
|
+
const Heading = `h${headingLevel}`;
|
|
89
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
90
|
+
Heading,
|
|
91
|
+
{
|
|
92
|
+
ref,
|
|
93
|
+
className: cn("seeds-narrative-headline", className),
|
|
94
|
+
...props,
|
|
95
|
+
children
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
NarrativeHeadline.displayName = "NarrativeHeadline";
|
|
100
|
+
var NarrativeHeadline_default = NarrativeHeadline;
|
|
101
|
+
|
|
102
|
+
// src/NarrativeHeadline/NarrativeHeadlineHighlight.tsx
|
|
103
|
+
var React3 = __toESM(require("react"));
|
|
104
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
105
|
+
var NarrativeHeadlineHighlight = React3.forwardRef(({ children, className, ...props }, ref) => {
|
|
106
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
107
|
+
"span",
|
|
108
|
+
{
|
|
109
|
+
ref,
|
|
110
|
+
className: cn("seeds-narrative-headline-highlight", className),
|
|
111
|
+
...props,
|
|
112
|
+
children
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
NarrativeHeadlineHighlight.displayName = "NarrativeHeadlineHighlight";
|
|
117
|
+
var NarrativeHeadlineHighlight_default = NarrativeHeadlineHighlight;
|
|
118
|
+
|
|
119
|
+
// src/NarrativeHeadline/NarrativeHeadlineRoll.tsx
|
|
120
|
+
var React4 = __toESM(require("react"));
|
|
121
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
122
|
+
var NarrativeHeadlineRoll = React4.forwardRef(
|
|
123
|
+
({
|
|
124
|
+
words,
|
|
125
|
+
intervalMs = 2200,
|
|
126
|
+
transitionMs = 500,
|
|
127
|
+
direction = "up",
|
|
128
|
+
gradient = true,
|
|
129
|
+
className,
|
|
130
|
+
style,
|
|
131
|
+
...rest
|
|
132
|
+
}, ref) => {
|
|
133
|
+
const [i, setI] = React4.useState(0);
|
|
134
|
+
React4.useEffect(() => {
|
|
135
|
+
if (words.length <= 1) return;
|
|
136
|
+
const reduced = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
137
|
+
if (reduced) return;
|
|
138
|
+
const t = setInterval(
|
|
139
|
+
() => setI((j) => (j + 1) % words.length),
|
|
140
|
+
intervalMs
|
|
141
|
+
);
|
|
142
|
+
return () => clearInterval(t);
|
|
143
|
+
}, [intervalMs, words.length]);
|
|
144
|
+
const merged = {
|
|
145
|
+
...style,
|
|
146
|
+
// Consumed by the transition timing in narrative-headline.css.
|
|
147
|
+
["--seeds-narrative-headline-roll-ms"]: `${transitionMs}ms`
|
|
148
|
+
};
|
|
149
|
+
const prev = words.length ? (i - 1 + words.length) % words.length : 0;
|
|
150
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
151
|
+
"span",
|
|
152
|
+
{
|
|
153
|
+
ref,
|
|
154
|
+
className: cn(
|
|
155
|
+
"seeds-narrative-headline-roll",
|
|
156
|
+
{ "seeds-narrative-headline-roll-down": direction === "down" },
|
|
157
|
+
{ "seeds-narrative-headline-roll-gradient": gradient },
|
|
158
|
+
className
|
|
159
|
+
),
|
|
160
|
+
style: merged,
|
|
161
|
+
...rest,
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "seeds-narrative-headline-roll-sizer", "aria-hidden": "true", children: words[i] }),
|
|
164
|
+
words.map((w, idx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
165
|
+
"span",
|
|
166
|
+
{
|
|
167
|
+
className: cn("seeds-narrative-headline-roll-word", {
|
|
168
|
+
"seeds-narrative-headline-roll-word-active": idx === i,
|
|
169
|
+
"seeds-narrative-headline-roll-word-past": idx === prev && i !== prev
|
|
170
|
+
}),
|
|
171
|
+
"aria-hidden": idx === i ? void 0 : "true",
|
|
172
|
+
children: w
|
|
173
|
+
},
|
|
174
|
+
idx
|
|
175
|
+
))
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
NarrativeHeadlineRoll.displayName = "NarrativeHeadlineRoll";
|
|
182
|
+
var NarrativeHeadlineRoll_default = NarrativeHeadlineRoll;
|
|
183
|
+
|
|
184
|
+
// src/PullQuote/PullQuote.tsx
|
|
185
|
+
var React5 = __toESM(require("react"));
|
|
186
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
187
|
+
var PullQuote = React5.forwardRef(
|
|
188
|
+
({ quote, attribution, appearance = "subtle", className, ...props }, ref) => {
|
|
189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
190
|
+
"figure",
|
|
191
|
+
{
|
|
192
|
+
ref,
|
|
193
|
+
className: cn(
|
|
194
|
+
"seeds-pull-quote",
|
|
195
|
+
{ "seeds-pull-quote-vivid": appearance === "vivid" },
|
|
196
|
+
className
|
|
197
|
+
),
|
|
198
|
+
...props,
|
|
199
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "seeds-pull-quote-bar", children: [
|
|
200
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("blockquote", { className: "seeds-pull-quote-text", children: quote }),
|
|
201
|
+
attribution != null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("figcaption", { className: "seeds-pull-quote-attribution", children: attribution })
|
|
202
|
+
] })
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
PullQuote.displayName = "PullQuote";
|
|
208
|
+
var PullQuote_default = PullQuote;
|
|
209
|
+
|
|
210
|
+
// src/EyebrowToken/EyebrowToken.tsx
|
|
211
|
+
var React6 = __toESM(require("react"));
|
|
212
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
213
|
+
var EyebrowToken = React6.forwardRef(
|
|
214
|
+
({ children, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
215
|
+
"span",
|
|
216
|
+
{
|
|
217
|
+
ref,
|
|
218
|
+
className: cn("seeds-eyebrow-token", className),
|
|
219
|
+
...props,
|
|
220
|
+
children
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
);
|
|
224
|
+
EyebrowToken.displayName = "EyebrowToken";
|
|
225
|
+
var EyebrowToken_default = EyebrowToken;
|
|
226
|
+
|
|
227
|
+
// src/MetricHighlight/MetricHighlight.tsx
|
|
228
|
+
var React7 = __toESM(require("react"));
|
|
229
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
230
|
+
var SPARK_VIEWBOX_WIDTH = 100;
|
|
231
|
+
var SPARK_VIEWBOX_HEIGHT = 32;
|
|
232
|
+
var SPARK_PADDING = 2;
|
|
233
|
+
var DEFAULT_SPARK = [6, 10, 8, 13, 11, 16];
|
|
234
|
+
var smoothPath = (points) => {
|
|
235
|
+
if (points.length < 2) {
|
|
236
|
+
const p = points[0];
|
|
237
|
+
return p ? `M ${p.x},${p.y}` : "";
|
|
238
|
+
}
|
|
239
|
+
let d = `M ${points[0].x.toFixed(2)},${points[0].y.toFixed(2)}`;
|
|
240
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
241
|
+
const p1 = points[i];
|
|
242
|
+
const p2 = points[i + 1];
|
|
243
|
+
const p0 = points[i - 1] ?? p1;
|
|
244
|
+
const p3 = points[i + 2] ?? p2;
|
|
245
|
+
const cp1x = p1.x + (p2.x - p0.x) / 6;
|
|
246
|
+
const cp1y = p1.y + (p2.y - p0.y) / 6;
|
|
247
|
+
const cp2x = p2.x - (p3.x - p1.x) / 6;
|
|
248
|
+
const cp2y = p2.y - (p3.y - p1.y) / 6;
|
|
249
|
+
d += ` C ${cp1x.toFixed(2)},${cp1y.toFixed(2)} ${cp2x.toFixed(
|
|
250
|
+
2
|
|
251
|
+
)},${cp2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;
|
|
252
|
+
}
|
|
253
|
+
return d;
|
|
254
|
+
};
|
|
255
|
+
var Sparkline = ({
|
|
256
|
+
data,
|
|
257
|
+
negative
|
|
258
|
+
}) => {
|
|
259
|
+
const min = Math.min(...data);
|
|
260
|
+
const max = Math.max(...data);
|
|
261
|
+
const range = max - min || 1;
|
|
262
|
+
const usableHeight = SPARK_VIEWBOX_HEIGHT - SPARK_PADDING * 2;
|
|
263
|
+
const points = data.map((value, i) => ({
|
|
264
|
+
x: data.length === 1 ? SPARK_VIEWBOX_WIDTH / 2 : i / (data.length - 1) * SPARK_VIEWBOX_WIDTH,
|
|
265
|
+
// Invert y so larger values sit higher in the chart.
|
|
266
|
+
y: SPARK_PADDING + (1 - (value - min) / range) * usableHeight
|
|
267
|
+
}));
|
|
268
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
269
|
+
"svg",
|
|
270
|
+
{
|
|
271
|
+
className: cn("seeds-metric-highlight-spark", {
|
|
272
|
+
"seeds-metric-highlight-spark-negative": Boolean(negative)
|
|
273
|
+
}),
|
|
274
|
+
viewBox: `0 0 ${SPARK_VIEWBOX_WIDTH} ${SPARK_VIEWBOX_HEIGHT}`,
|
|
275
|
+
preserveAspectRatio: "none",
|
|
276
|
+
"aria-hidden": true,
|
|
277
|
+
focusable: false,
|
|
278
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: smoothPath(points), fill: "none", strokeLinecap: "round" })
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
};
|
|
282
|
+
var TrendArrow = ({ direction }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
283
|
+
"svg",
|
|
284
|
+
{
|
|
285
|
+
className: "seeds-metric-highlight-trend-arrow",
|
|
286
|
+
viewBox: "0 0 16 16",
|
|
287
|
+
"aria-hidden": true,
|
|
288
|
+
focusable: false,
|
|
289
|
+
children: direction === "up" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
290
|
+
"path",
|
|
291
|
+
{
|
|
292
|
+
d: "M4.5 11.5 11.5 4.5M11.5 4.5H5.5M11.5 4.5V10.5",
|
|
293
|
+
fill: "none",
|
|
294
|
+
strokeLinecap: "round",
|
|
295
|
+
strokeLinejoin: "round"
|
|
296
|
+
}
|
|
297
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
298
|
+
"path",
|
|
299
|
+
{
|
|
300
|
+
d: "M4.5 4.5 11.5 11.5M11.5 11.5H5.5M11.5 11.5V5.5",
|
|
301
|
+
fill: "none",
|
|
302
|
+
strokeLinecap: "round",
|
|
303
|
+
strokeLinejoin: "round"
|
|
304
|
+
}
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
var TrendBadge = ({ direction = "up", value, ...rest }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
309
|
+
"span",
|
|
310
|
+
{
|
|
311
|
+
className: cn("seeds-metric-highlight-trend", {
|
|
312
|
+
"seeds-metric-highlight-trend-down": direction === "down"
|
|
313
|
+
}),
|
|
314
|
+
...rest,
|
|
315
|
+
children: [
|
|
316
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TrendArrow, { direction }),
|
|
317
|
+
value != null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "seeds-metric-highlight-trend-value", children: value })
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
var MetricHighlight = React7.forwardRef(
|
|
322
|
+
({
|
|
323
|
+
label,
|
|
324
|
+
value,
|
|
325
|
+
trend,
|
|
326
|
+
data,
|
|
327
|
+
chart = true,
|
|
328
|
+
appearance = "horizontal",
|
|
329
|
+
size = "default",
|
|
330
|
+
container = true,
|
|
331
|
+
className,
|
|
332
|
+
...props
|
|
333
|
+
}, ref) => {
|
|
334
|
+
const isDown = trend?.direction === "down";
|
|
335
|
+
const resolvedData = data ?? (isDown ? [...DEFAULT_SPARK].reverse() : DEFAULT_SPARK);
|
|
336
|
+
const showChart = chart && resolvedData.length > 0;
|
|
337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
338
|
+
"div",
|
|
339
|
+
{
|
|
340
|
+
ref,
|
|
341
|
+
className: cn(
|
|
342
|
+
"seeds-metric-highlight",
|
|
343
|
+
{
|
|
344
|
+
"seeds-metric-highlight-vertical": appearance === "vertical",
|
|
345
|
+
"seeds-metric-highlight-small": size === "small",
|
|
346
|
+
"seeds-metric-highlight-bare": !container
|
|
347
|
+
},
|
|
348
|
+
className
|
|
349
|
+
),
|
|
350
|
+
...props,
|
|
351
|
+
children: [
|
|
352
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "seeds-metric-highlight-metric", children: [
|
|
353
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "seeds-metric-highlight-label", children: label }),
|
|
354
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "seeds-metric-highlight-value-row", children: [
|
|
355
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "seeds-metric-highlight-value", children: value }),
|
|
356
|
+
trend != null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TrendBadge, { ...trend })
|
|
357
|
+
] })
|
|
358
|
+
] }),
|
|
359
|
+
showChart && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sparkline, { data: resolvedData, negative: isDown })
|
|
360
|
+
]
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
MetricHighlight.displayName = "MetricHighlight";
|
|
366
|
+
var MetricHighlight_default = MetricHighlight;
|
|
73
367
|
// Annotate the CommonJS export names for ESM import in node:
|
|
74
368
|
0 && (module.exports = {
|
|
75
|
-
|
|
369
|
+
EyebrowToken,
|
|
370
|
+
MetricHighlight,
|
|
371
|
+
NarrativeContainer,
|
|
372
|
+
NarrativeHeadline,
|
|
373
|
+
NarrativeHeadlineHighlight,
|
|
374
|
+
NarrativeHeadlineRoll,
|
|
375
|
+
PullQuote
|
|
76
376
|
});
|
|
77
377
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/NarrativeContainer/NarrativeContainer.tsx"],"sourcesContent":["/* Narrative Kit — composable narrative primitives.\n * Each primitive adds a single line below. No default export — this is a kit. */\nexport {\n NarrativeContainer,\n type TypeNarrativeContainerProps,\n} from \"./NarrativeContainer\";\n\nexport type { TypeNarrativeTone } from \"./_internal/types\";\n","import * as React from \"react\";\nimport type { TypeNarrativeContainerProps } from \"./NarrativeContainerTypes\";\n\n/** Merge class names, supporting `{ \"class\": boolean }` toggles. */\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\n/**\n * NarrativeContainer — the foundational surface that narrative primitives are\n * composed inside. A padded, rounded, elevated card with a content slot and an\n * optional decorative gradient accent that wraps the top-left corner.\n *\n * Styled via `narrative-container.css` (Seeds CSS custom properties). Consumers\n * import the classes through `@sproutsocial/racine/css/components`. Surface\n * overrides are done with standard `className` / `style`.\n */\nconst NarrativeContainer = React.forwardRef<\n HTMLDivElement,\n TypeNarrativeContainerProps\n>(({ children, accent = true, className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\n \"seeds-narrative-container\",\n { \"seeds-narrative-container-accent\": accent },\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nNarrativeContainer.displayName = \"NarrativeContainer\";\n\nexport default NarrativeContainer;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AAwCnB;AApCJ,SAAS,MACJ,QACK;AACR,QAAM,UAAoB,CAAC;AAE3B,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,MAAO;AAEZ,QAAI,OAAO,UAAU,UAAU;AAC7B,cAAQ,KAAK,KAAK;AAAA,IACpB,WAAW,OAAO,UAAU,UAAU;AACpC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,YAAI,OAAO;AACT,kBAAQ,KAAK,GAAG;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,QAAQ,KAAK,GAAG;AACzB;AAWA,IAAM,qBAA2B,iBAG/B,CAAC,EAAE,UAAU,SAAS,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ;AAC3D,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,EAAE,oCAAoC,OAAO;AAAA,QAC7C;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ,CAAC;AAED,mBAAmB,cAAc;AAEjC,IAAO,6BAAQ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/NarrativeContainer/NarrativeContainer.tsx","../src/_internal/cn.ts","../src/NarrativeHeadline/NarrativeHeadline.tsx","../src/NarrativeHeadline/NarrativeHeadlineHighlight.tsx","../src/NarrativeHeadline/NarrativeHeadlineRoll.tsx","../src/PullQuote/PullQuote.tsx","../src/EyebrowToken/EyebrowToken.tsx","../src/MetricHighlight/MetricHighlight.tsx"],"sourcesContent":["/* Narrative Kit — composable narrative primitives.\n * Each primitive adds a single line below. No default export — this is a kit. */\nexport {\n NarrativeContainer,\n type TypeNarrativeContainerProps,\n} from \"./NarrativeContainer\";\n\nexport {\n NarrativeHeadline,\n NarrativeHeadlineHighlight,\n NarrativeHeadlineRoll,\n type TypeNarrativeHeadlineProps,\n type TypeNarrativeHeadlineLevel,\n type TypeNarrativeHeadlineHighlightProps,\n type TypeNarrativeHeadlineRollProps,\n} from \"./NarrativeHeadline\";\n\nexport { PullQuote, type TypePullQuoteProps } from \"./PullQuote\";\n\nexport { EyebrowToken, type TypeEyebrowTokenProps } from \"./EyebrowToken\";\n\nexport {\n MetricHighlight,\n type TypeMetricHighlightProps,\n type TypeMetricHighlightTrend,\n} from \"./MetricHighlight\";\n\nexport type { TypeNarrativeTone } from \"./_internal/types\";\n","import * as React from \"react\";\nimport { cn } from \"../_internal/cn\";\nimport type { TypeNarrativeContainerProps } from \"./NarrativeContainerTypes\";\n\n/**\n * NarrativeContainer — the foundational surface that narrative primitives are\n * composed inside. A padded, rounded, elevated card with a content slot and an\n * optional decorative gradient accent that wraps the top-left corner.\n *\n * Styled via `narrative-container.css` (Seeds CSS custom properties). Consumers\n * import the classes through `@sproutsocial/racine/css/components`. Surface\n * overrides are done with standard `className` / `style`.\n */\nconst NarrativeContainer = React.forwardRef<\n HTMLDivElement,\n TypeNarrativeContainerProps\n>(({ children, accent = true, className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\n \"seeds-narrative-container\",\n { \"seeds-narrative-container-accent\": accent },\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nNarrativeContainer.displayName = \"NarrativeContainer\";\n\nexport default NarrativeContainer;\n","/** Merge class names, supporting `{ \"class\": boolean }` toggles. */\nexport function cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n","import * as React from \"react\";\nimport { cn } from \"../_internal/cn\";\nimport type { TypeNarrativeHeadlineProps } from \"./NarrativeHeadlineTypes\";\n\n/**\n * NarrativeHeadline — the titling primitive for a narrative block. A single\n * heading element, nothing more: supporting copy and tags/tokens belong to\n * sibling primitives that compose alongside it, not baked into the title.\n *\n * Wrap part of the headline in `<NarrativeHeadlineHighlight>` for a static\n * gradient accent, or `<NarrativeHeadlineRoll>` for the rolling \"roll\" variant.\n *\n * Styled via `narrative-headline.css` (Seeds CSS custom properties). Consumers\n * import the classes through `@sproutsocial/racine/css/components`. Overrides\n * are done with standard `className` / `style`.\n */\nconst NarrativeHeadline = React.forwardRef<\n HTMLHeadingElement,\n TypeNarrativeHeadlineProps\n>(({ children, headingLevel = 2, className, ...props }, ref) => {\n const Heading = `h${headingLevel}` as const;\n\n return (\n <Heading\n ref={ref}\n className={cn(\"seeds-narrative-headline\", className)}\n {...props}\n >\n {children}\n </Heading>\n );\n});\n\nNarrativeHeadline.displayName = \"NarrativeHeadline\";\n\nexport default NarrativeHeadline;\n","import * as React from \"react\";\nimport { cn } from \"../_internal/cn\";\nimport type { TypeNarrativeHeadlineHighlightProps } from \"./NarrativeHeadlineTypes\";\n\n/**\n * NarrativeHeadlineHighlight — paints a span of headline text with the AI\n * brand gradient (the \"highlighted word\" treatment). Drop it inline inside a\n * `<NarrativeHeadline>`:\n *\n * <NarrativeHeadline>\n * Headline with a <NarrativeHeadlineHighlight>highlighted</NarrativeHeadlineHighlight> word\n * </NarrativeHeadline>\n */\nconst NarrativeHeadlineHighlight = React.forwardRef<\n HTMLSpanElement,\n TypeNarrativeHeadlineHighlightProps\n>(({ children, className, ...props }, ref) => {\n return (\n <span\n ref={ref}\n className={cn(\"seeds-narrative-headline-highlight\", className)}\n {...props}\n >\n {children}\n </span>\n );\n});\n\nNarrativeHeadlineHighlight.displayName = \"NarrativeHeadlineHighlight\";\n\nexport default NarrativeHeadlineHighlight;\n","import * as React from \"react\";\nimport { cn } from \"../_internal/cn\";\nimport type { TypeNarrativeHeadlineRollProps } from \"./NarrativeHeadlineTypes\";\n\n/**\n * NarrativeHeadlineRoll — the \"roll\" variant: a vertical word roll for the\n * highlighted slot of a headline. The active word slides into view while the\n * previous one rolls out (no typing). Inspired by the performative-ui WordRoll.\n *\n * <NarrativeHeadline>\n * Built for{\" \"}\n * <NarrativeHeadlineRoll words={[\"marketers\", \"agencies\", \"teams\"]} />\n * </NarrativeHeadline>\n *\n * The container width follows the active word so it sits naturally inline.\n * Gradient-painted by default; honors `prefers-reduced-motion` by holding the\n * first word static.\n */\nconst NarrativeHeadlineRoll = React.forwardRef<\n HTMLSpanElement,\n TypeNarrativeHeadlineRollProps\n>(\n (\n {\n words,\n intervalMs = 2200,\n transitionMs = 500,\n direction = \"up\",\n gradient = true,\n className,\n style,\n ...rest\n },\n ref\n ) => {\n const [i, setI] = React.useState(0);\n\n React.useEffect(() => {\n if (words.length <= 1) return;\n\n // Respect reduced-motion: hold the first word, skip the cycle entirely.\n const reduced =\n typeof window !== \"undefined\" &&\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n if (reduced) return;\n\n const t = setInterval(\n () => setI((j) => (j + 1) % words.length),\n intervalMs\n );\n return () => clearInterval(t);\n }, [intervalMs, words.length]);\n\n const merged: React.CSSProperties = {\n ...style,\n // Consumed by the transition timing in narrative-headline.css.\n [\"--seeds-narrative-headline-roll-ms\" as string]: `${transitionMs}ms`,\n };\n\n const prev = words.length ? (i - 1 + words.length) % words.length : 0;\n\n return (\n <span\n ref={ref}\n className={cn(\n \"seeds-narrative-headline-roll\",\n { \"seeds-narrative-headline-roll-down\": direction === \"down\" },\n { \"seeds-narrative-headline-roll-gradient\": gradient },\n className\n )}\n style={merged}\n {...rest}\n >\n {/* Sizer keeps the container width matched to the active word — without\n it the absolutely-positioned words would collapse the box. */}\n <span className=\"seeds-narrative-headline-roll-sizer\" aria-hidden=\"true\">\n {words[i]}\n </span>\n {words.map((w, idx) => (\n <span\n key={idx}\n className={cn(\"seeds-narrative-headline-roll-word\", {\n \"seeds-narrative-headline-roll-word-active\": idx === i,\n \"seeds-narrative-headline-roll-word-past\":\n idx === prev && i !== prev,\n })}\n aria-hidden={idx === i ? undefined : \"true\"}\n >\n {w}\n </span>\n ))}\n </span>\n );\n }\n);\n\nNarrativeHeadlineRoll.displayName = \"NarrativeHeadlineRoll\";\n\nexport default NarrativeHeadlineRoll;\n","import * as React from \"react\";\nimport { cn } from \"../_internal/cn\";\nimport type { TypePullQuoteProps } from \"./PullQuoteTypes\";\n\n/**\n * PullQuote — a narrative primitive that spotlights a quotation on an AI\n * gradient surface. The quote sits behind an accented vertical bar with an\n * optional attribution row (author name, avatar, network).\n *\n * Rendered as semantic `<figure>` / `<blockquote>` / `<figcaption>` markup.\n * Styled via `pull-quote.css` (Seeds CSS custom properties); consumers import\n * the classes through `@sproutsocial/racine/css/components`. Surface overrides\n * are done with standard `className` / `style`.\n */\nconst PullQuote = React.forwardRef<HTMLElement, TypePullQuoteProps>(\n ({ quote, attribution, appearance = \"subtle\", className, ...props }, ref) => {\n return (\n <figure\n ref={ref}\n className={cn(\n \"seeds-pull-quote\",\n { \"seeds-pull-quote-vivid\": appearance === \"vivid\" },\n className\n )}\n {...props}\n >\n <div className=\"seeds-pull-quote-bar\">\n <blockquote className=\"seeds-pull-quote-text\">{quote}</blockquote>\n {attribution != null && (\n <figcaption className=\"seeds-pull-quote-attribution\">\n {attribution}\n </figcaption>\n )}\n </div>\n </figure>\n );\n }\n);\n\nPullQuote.displayName = \"PullQuote\";\n\nexport default PullQuote;\n","import * as React from \"react\";\nimport { cn } from \"../_internal/cn\";\nimport type { TypeEyebrowTokenProps } from \"./EyebrowTokenTypes\";\n\n/**\n * EyebrowToken — a small gray pill that sits above a headline to carry a short\n * bit of context: a date (e.g. \"May 31, 2026\"), category, or kicker.\n *\n * A faithful match of the Figma \"Token / Non Clickable / Default\" design — a\n * solid `#dee1e1` fill with a matching border, 6px radius, and small body text.\n * Styled via `eyebrow-token.css` (Seeds CSS custom properties); consumers import\n * the classes through `@sproutsocial/racine/css/components`. Overrides come via\n * standard `className` / `style`.\n */\nconst EyebrowToken = React.forwardRef<HTMLSpanElement, TypeEyebrowTokenProps>(\n ({ children, className, ...props }, ref) => (\n <span\n ref={ref}\n className={cn(\"seeds-eyebrow-token\", className)}\n {...props}\n >\n {children}\n </span>\n )\n);\n\nEyebrowToken.displayName = \"EyebrowToken\";\n\nexport default EyebrowToken;\n","import * as React from \"react\";\nimport { cn } from \"../_internal/cn\";\nimport type {\n TypeMetricHighlightProps,\n TypeMetricHighlightTrend,\n} from \"./MetricHighlightTypes\";\n\n/** Fixed viewBox the sparkline normalizes into; CSS stretches it to fit. */\nconst SPARK_VIEWBOX_WIDTH = 100;\nconst SPARK_VIEWBOX_HEIGHT = 32;\n/** Inset so the stroke at the min/max points isn't clipped by the viewBox edge. */\nconst SPARK_PADDING = 2;\n\n/**\n * Default sparkline series used when the consumer doesn't pass `data` — a gently\n * rising wave. It's reversed for a downward trend so the line slopes to match.\n */\nconst DEFAULT_SPARK = [6, 10, 8, 13, 11, 16];\n\n/**\n * Build a smooth SVG path through `points` using a Catmull-Rom spline converted\n * to cubic béziers. Each segment's control points are derived from its\n * neighbors, so the curve passes through every data point with soft, rounded\n * transitions (matching the design) rather than sharp polyline corners.\n */\nconst smoothPath = (points: { x: number; y: number }[]) => {\n if (points.length < 2) {\n const p = points[0];\n return p ? `M ${p.x},${p.y}` : \"\";\n }\n\n let d = `M ${points[0]!.x.toFixed(2)},${points[0]!.y.toFixed(2)}`;\n for (let i = 0; i < points.length - 1; i++) {\n // The loop bound guarantees p1 and p2; p0/p3 clamp to the endpoints.\n const p1 = points[i]!;\n const p2 = points[i + 1]!;\n const p0 = points[i - 1] ?? p1;\n const p3 = points[i + 2] ?? p2;\n // Catmull-Rom → bézier control points (tension 1/6).\n const cp1x = p1.x + (p2.x - p0.x) / 6;\n const cp1y = p1.y + (p2.y - p0.y) / 6;\n const cp2x = p2.x - (p3.x - p1.x) / 6;\n const cp2y = p2.y - (p3.y - p1.y) / 6;\n d += ` C ${cp1x.toFixed(2)},${cp1y.toFixed(2)} ${cp2x.toFixed(\n 2\n )},${cp2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;\n }\n return d;\n};\n\n/**\n * Sparkline — an inline SVG path normalized from `data` into a fixed viewBox and\n * smoothed into a soft curve. `preserveAspectRatio=\"none\"` plus a non-scaling\n * stroke (set in CSS) lets a single path stretch to any width/height while\n * keeping a crisp line.\n */\nconst Sparkline = ({\n data,\n negative,\n}: {\n data: number[];\n negative?: boolean;\n}) => {\n const min = Math.min(...data);\n const max = Math.max(...data);\n const range = max - min || 1;\n const usableHeight = SPARK_VIEWBOX_HEIGHT - SPARK_PADDING * 2;\n\n const points = data.map((value, i) => ({\n x:\n data.length === 1\n ? SPARK_VIEWBOX_WIDTH / 2\n : (i / (data.length - 1)) * SPARK_VIEWBOX_WIDTH,\n // Invert y so larger values sit higher in the chart.\n y: SPARK_PADDING + (1 - (value - min) / range) * usableHeight,\n }));\n\n return (\n <svg\n className={cn(\"seeds-metric-highlight-spark\", {\n \"seeds-metric-highlight-spark-negative\": Boolean(negative),\n })}\n viewBox={`0 0 ${SPARK_VIEWBOX_WIDTH} ${SPARK_VIEWBOX_HEIGHT}`}\n preserveAspectRatio=\"none\"\n aria-hidden\n focusable={false}\n >\n <path d={smoothPath(points)} fill=\"none\" strokeLinecap=\"round\" />\n </svg>\n );\n};\n\n/** Directional arrow glyph for the trend badge (decorative; `aria-hidden`). */\nconst TrendArrow = ({ direction }: { direction: \"up\" | \"down\" }) => (\n <svg\n className=\"seeds-metric-highlight-trend-arrow\"\n viewBox=\"0 0 16 16\"\n aria-hidden\n focusable={false}\n >\n {direction === \"up\" ? (\n <path\n d=\"M4.5 11.5 11.5 4.5M11.5 4.5H5.5M11.5 4.5V10.5\"\n fill=\"none\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n ) : (\n <path\n d=\"M4.5 4.5 11.5 11.5M11.5 11.5H5.5M11.5 11.5V5.5\"\n fill=\"none\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n )}\n </svg>\n);\n\nconst TrendBadge = ({ direction = \"up\", value, ...rest }: TypeMetricHighlightTrend) => (\n <span\n className={cn(\"seeds-metric-highlight-trend\", {\n \"seeds-metric-highlight-trend-down\": direction === \"down\",\n })}\n {...rest}\n >\n <TrendArrow direction={direction} />\n {value != null && (\n <span className=\"seeds-metric-highlight-trend-value\">{value}</span>\n )}\n </span>\n);\n\n/**\n * MetricHighlight — a narrative primitive that spotlights a single metric: a\n * label, a large monospace value, an optional trend badge (a directional arrow\n * plus an optional number such as \"240%\"), and an optional sparkline.\n *\n * Styled via `metric-highlight.css` (Seeds CSS custom properties); consumers\n * import the classes through `@sproutsocial/racine/css/components`. Layout flexes\n * across `appearance` (horizontal/vertical) and `size` (default/small); surface\n * overrides are done with standard `className` / `style`.\n */\nconst MetricHighlight = React.forwardRef<HTMLDivElement, TypeMetricHighlightProps>(\n (\n {\n label,\n value,\n trend,\n data,\n chart = true,\n appearance = \"horizontal\",\n size = \"default\",\n container = true,\n className,\n ...props\n },\n ref\n ) => {\n const isDown = trend?.direction === \"down\";\n // Fall back to a sensible default series, sloped to match the trend, so the\n // chart renders without the consumer wiring up data. An explicit `data`\n // (including an empty array to opt out) always wins.\n const resolvedData =\n data ?? (isDown ? [...DEFAULT_SPARK].reverse() : DEFAULT_SPARK);\n const showChart = chart && resolvedData.length > 0;\n\n return (\n <div\n ref={ref}\n className={cn(\n \"seeds-metric-highlight\",\n {\n \"seeds-metric-highlight-vertical\": appearance === \"vertical\",\n \"seeds-metric-highlight-small\": size === \"small\",\n \"seeds-metric-highlight-bare\": !container,\n },\n className\n )}\n {...props}\n >\n <div className=\"seeds-metric-highlight-metric\">\n <p className=\"seeds-metric-highlight-label\">{label}</p>\n <div className=\"seeds-metric-highlight-value-row\">\n <p className=\"seeds-metric-highlight-value\">{value}</p>\n {trend != null && <TrendBadge {...trend} />}\n </div>\n </div>\n {showChart && <Sparkline data={resolvedData} negative={isDown} />}\n </div>\n );\n }\n);\n\nMetricHighlight.displayName = \"MetricHighlight\";\n\nexport default MetricHighlight;\n"],"mappings":"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","names":["React","import_jsx_runtime","React","import_jsx_runtime","React","import_jsx_runtime","React","import_jsx_runtime","React","import_jsx_runtime","React","import_jsx_runtime"]}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeds MetricHighlight component classes.
|
|
3
|
+
* Use these instead of writing out individual Tailwind utility classes.
|
|
4
|
+
*
|
|
5
|
+
* Requires @sproutsocial/seeds-react-theme/dist/theme-all.css imported for
|
|
6
|
+
* CSS variable definitions and dark mode support.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* <div class="seeds-metric-highlight">…</div> // horizontal, default
|
|
10
|
+
* <div class="seeds-metric-highlight seeds-metric-highlight-vertical">…</div>
|
|
11
|
+
* <div class="seeds-metric-highlight seeds-metric-highlight-small">…</div>
|
|
12
|
+
* <div class="seeds-metric-highlight seeds-metric-highlight-bare">…</div> // no surface
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
@layer components {
|
|
16
|
+
|
|
17
|
+
.seeds-metric-highlight {
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: flex-start;
|
|
21
|
+
gap: var(--space-350); /* 12px */
|
|
22
|
+
border-radius: var(--radius-800); /* 12px */
|
|
23
|
+
font-family: var(--font-family);
|
|
24
|
+
/* Container (default): a padded gray app surface (matches the design's
|
|
25
|
+
#f3f4f4 box). `-bare` strips it. */
|
|
26
|
+
padding: var(--space-400) var(--space-450); /* 16px / 24px */
|
|
27
|
+
background-color: var(--color-app-bg-base);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.seeds-metric-highlight-bare {
|
|
31
|
+
padding: 0;
|
|
32
|
+
background-color: transparent;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.seeds-metric-highlight-vertical {
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* The metric block: label + value/trend. Grows to fill the row in horizontal. */
|
|
40
|
+
.seeds-metric-highlight-metric {
|
|
41
|
+
box-sizing: border-box;
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
align-items: flex-start;
|
|
45
|
+
gap: var(--space-200); /* 4px */
|
|
46
|
+
flex: 1 0 0;
|
|
47
|
+
min-width: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.seeds-metric-highlight-vertical .seeds-metric-highlight-metric {
|
|
51
|
+
width: 100%;
|
|
52
|
+
flex: 0 0 auto;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Small: label and value collapse onto a single centered row. */
|
|
56
|
+
.seeds-metric-highlight-small .seeds-metric-highlight-metric {
|
|
57
|
+
flex-direction: row;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: var(--space-300); /* 8px */
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.seeds-metric-highlight-label {
|
|
63
|
+
margin: 0;
|
|
64
|
+
font-size: var(--font-size-300); /* 16px */
|
|
65
|
+
line-height: var(--line-height-400); /* ~28px */
|
|
66
|
+
font-weight: var(--font-weight-normal);
|
|
67
|
+
color: var(--color-text-body);
|
|
68
|
+
overflow-wrap: break-word;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* In small, the label takes the slack so the value/trend align to the end. */
|
|
72
|
+
.seeds-metric-highlight-small .seeds-metric-highlight-label {
|
|
73
|
+
flex: 1 0 0;
|
|
74
|
+
min-width: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.seeds-metric-highlight-value-row {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
gap: var(--space-300); /* 8px */
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.seeds-metric-highlight-small .seeds-metric-highlight-value-row {
|
|
84
|
+
justify-content: flex-end;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.seeds-metric-highlight-value {
|
|
88
|
+
margin: 0;
|
|
89
|
+
/* No mono token exists yet; fall back to a system monospace stack. Mirrors the
|
|
90
|
+
font/font-mono treatment in the design. */
|
|
91
|
+
font-family: var(--font-family-mono, ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace);
|
|
92
|
+
font-size: var(--font-size-700); /* 32px */
|
|
93
|
+
line-height: var(--line-height-700); /* 40px */
|
|
94
|
+
font-weight: var(--font-weight-bold);
|
|
95
|
+
color: var(--color-text-headline);
|
|
96
|
+
white-space: nowrap;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.seeds-metric-highlight-small .seeds-metric-highlight-value {
|
|
100
|
+
font-size: var(--font-size-600); /* 24px */
|
|
101
|
+
line-height: var(--line-height-600); /* 32px */
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Trend badge — a rounded pill. Up is the positive (green) treatment; down
|
|
105
|
+
swaps to the negative (red) treatment. Color flows to the arrow + value via
|
|
106
|
+
currentColor. */
|
|
107
|
+
.seeds-metric-highlight-trend {
|
|
108
|
+
display: inline-flex;
|
|
109
|
+
align-items: center;
|
|
110
|
+
gap: var(--space-200); /* 4px */
|
|
111
|
+
padding: var(--space-100) var(--space-300); /* 2px / 8px */
|
|
112
|
+
border-radius: 999px;
|
|
113
|
+
font-size: var(--font-size-200); /* 13px */
|
|
114
|
+
line-height: var(--line-height-200);
|
|
115
|
+
font-weight: var(--font-weight-bold);
|
|
116
|
+
white-space: nowrap;
|
|
117
|
+
background-color: var(--color-container-bg-decorative-green);
|
|
118
|
+
color: var(--color-text-decorative-green);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.seeds-metric-highlight-trend-down {
|
|
122
|
+
background-color: var(--color-container-bg-decorative-red);
|
|
123
|
+
color: var(--color-text-decorative-red);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.seeds-metric-highlight-trend-arrow {
|
|
127
|
+
flex: 0 0 auto;
|
|
128
|
+
width: 1em;
|
|
129
|
+
height: 1em;
|
|
130
|
+
stroke: currentColor;
|
|
131
|
+
stroke-width: 1.5;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Sparkline — a single normalized polyline stretched to fit. The non-scaling
|
|
135
|
+
stroke keeps a crisp 2px line at any aspect ratio. */
|
|
136
|
+
.seeds-metric-highlight-spark {
|
|
137
|
+
display: block;
|
|
138
|
+
flex: 0 0 auto;
|
|
139
|
+
/* Matches the design's Data Viz / Categorical 01 line color (#0b968f). */
|
|
140
|
+
stroke: var(--color-teal-700);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Downward trend: the red analog at the same scale step (#db3e3e). */
|
|
144
|
+
.seeds-metric-highlight-spark-negative {
|
|
145
|
+
stroke: var(--color-red-700);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.seeds-metric-highlight-spark path {
|
|
149
|
+
stroke: inherit;
|
|
150
|
+
stroke-width: 2;
|
|
151
|
+
vector-effect: non-scaling-stroke;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Horizontal: a compact fixed-width chart beside the metric, matched to the
|
|
155
|
+
metric block's height. */
|
|
156
|
+
.seeds-metric-highlight:not(.seeds-metric-highlight-vertical) .seeds-metric-highlight-spark {
|
|
157
|
+
width: 104px;
|
|
158
|
+
align-self: stretch;
|
|
159
|
+
min-height: 40px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.seeds-metric-highlight-small:not(.seeds-metric-highlight-vertical) .seeds-metric-highlight-spark {
|
|
163
|
+
width: 88px;
|
|
164
|
+
height: 40px;
|
|
165
|
+
align-self: center;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Vertical: a full-width chart stacked beneath the metric. */
|
|
169
|
+
.seeds-metric-highlight-vertical .seeds-metric-highlight-spark {
|
|
170
|
+
width: 100%;
|
|
171
|
+
height: 96px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.seeds-metric-highlight-vertical.seeds-metric-highlight-small .seeds-metric-highlight-spark {
|
|
175
|
+
height: 56px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
}
|