@fanfare-io/fanfare-sdk-react 0.20.1 → 0.22.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/README.md +14 -0
- package/dist/components/primitives/countdown.d.ts +53 -14
- package/dist/components/widgets/experience-widget.d.ts +28 -0
- package/dist/components/widgets/internal/auction-ledger.d.ts +1 -12
- package/dist/components/widgets/internal/selection-branches.d.ts +0 -2
- package/dist/hooks/use-experience-journey.d.ts +6 -0
- package/dist/index-DMy2IUi5.js +8011 -0
- package/dist/index.js +1 -1
- package/dist/stripe-card-field-DaNEAuCE.js +175 -0
- package/dist/styles/base.css +1 -1
- package/dist/styles/index.d.ts +0 -1
- package/dist/styles/theme.css +449 -1
- package/dist/theme/font-loader.d.ts +58 -0
- package/dist/theme/theme-context.d.ts +8 -0
- package/package.json +6 -7
- package/dist/index-BRXy30hl.js +0 -7837
- package/dist/stripe-card-field-D9L3ASYF.js +0 -170
package/dist/styles/theme.css
CHANGED
|
@@ -55,6 +55,23 @@
|
|
|
55
55
|
|
|
56
56
|
[data-fanfare-variant="retro"] {
|
|
57
57
|
--ff-variant: "retro";
|
|
58
|
+
/* Inconsolata's advance is 0.5; 0.58 also covers the fallback monospace stack a host that
|
|
59
|
+
* does not load the face renders with. */
|
|
60
|
+
--ff-numeral-advance: 0.58;
|
|
61
|
+
--ff-countdown-cell-padding: 0.5rem 0.75rem;
|
|
62
|
+
--ff-countdown-cell-border-width: 2px;
|
|
63
|
+
--ff-countdown-reserve-cell: 1.75rem;
|
|
64
|
+
--ff-countdown-reserve-gap: 1rem;
|
|
65
|
+
--ff-countdown-cell-bg: var(--ff-color-background);
|
|
66
|
+
--ff-countdown-cell-shadow: 3px 3px 0 var(--ff-color-foreground);
|
|
67
|
+
--ff-progress-bar-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.2);
|
|
68
|
+
--ff-card-shadow: 6px 6px 0 var(--ff-color-foreground);
|
|
69
|
+
--ff-card-shadow-elevated: var(--ff-card-shadow);
|
|
70
|
+
--ff-countdown-separator-display: none;
|
|
71
|
+
--ff-countdown-inline-weight: 800;
|
|
72
|
+
--ff-countdown-tone-critical-bg: #ff3a00;
|
|
73
|
+
--ff-countdown-tone-critical-fg: #ffffff;
|
|
74
|
+
--ff-countdown-numeral-font: var(--ff-font-mono);
|
|
58
75
|
--ff-color-background: #f4f1ea;
|
|
59
76
|
--ff-color-foreground: #000000;
|
|
60
77
|
--ff-color-muted: #444444;
|
|
@@ -63,7 +80,7 @@
|
|
|
63
80
|
/*
|
|
64
81
|
* Note: retro intentionally does NOT override --ff-color-danger. The
|
|
65
82
|
* brutalist `#ff3a00` orange is a Countdown-critical-urgency accent only
|
|
66
|
-
* (
|
|
83
|
+
* (scoped to the countdown unit rule below), not a global semantic
|
|
67
84
|
* destructive color. Destructive buttons / error views keep the customer's
|
|
68
85
|
* (or @theme default) danger token under retro.
|
|
69
86
|
*/
|
|
@@ -74,15 +91,51 @@
|
|
|
74
91
|
|
|
75
92
|
[data-fanfare-variant="rounded"] {
|
|
76
93
|
--ff-variant: "rounded";
|
|
94
|
+
--ff-countdown-cell-radius: 14px;
|
|
95
|
+
--ff-countdown-cell-padding: 0.75rem 1rem;
|
|
96
|
+
--ff-countdown-cell-border-width: 1px;
|
|
97
|
+
--ff-countdown-cell-bg: var(--ff-color-background);
|
|
98
|
+
--ff-countdown-cell-shadow: 0 2px 12px -3px rgba(0, 0, 0, 0.08);
|
|
99
|
+
--ff-countdown-cell-shadow-hover: 0 4px 16px -4px rgba(0, 0, 0, 0.12);
|
|
100
|
+
--ff-countdown-separator-display: none;
|
|
101
|
+
--ff-countdown-inline-weight: 600;
|
|
102
|
+
--ff-countdown-reserve-cell: 2.125rem;
|
|
103
|
+
--ff-countdown-reserve-gap: 1rem;
|
|
104
|
+
--ff-card-shadow: 0 2px 12px -3px rgba(0, 0, 0, 0.08);
|
|
105
|
+
--ff-card-shadow-hover: 0 4px 16px -4px rgba(0, 0, 0, 0.12);
|
|
106
|
+
--ff-card-shadow-elevated: var(--ff-card-shadow);
|
|
107
|
+
--ff-card-shadow-elevated-hover: var(--ff-card-shadow-hover);
|
|
108
|
+
--ff-progress-shadow: inset 0 1px 4px -1px rgba(0, 0, 0, 0.06);
|
|
109
|
+
--ff-progress-bar-shadow: 0 0 10px -2px var(--ff-color-primary);
|
|
110
|
+
--ff-loading-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.08);
|
|
111
|
+
--ff-button-shadow: 0 2px 12px -3px rgba(0, 0, 0, 0.08);
|
|
112
|
+
--ff-button-shadow-hover: 0 4px 16px -4px rgba(0, 0, 0, 0.12);
|
|
113
|
+
--ff-button-shadow-active: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
114
|
+
--ff-button-shadow-primary: 0 2px 12px -3px rgba(0, 0, 0, 0.08), 0 0 12px -6px var(--ff-color-primary);
|
|
115
|
+
--ff-input-shadow: inset 0 1px 4px -1px rgba(0, 0, 0, 0.06);
|
|
116
|
+
--ff-input-shadow-focus: inset 0 1px 4px -1px rgba(0, 0, 0, 0.06), 0 0 0 3px rgba(var(--ff-color-primary-rgb), 0.12);
|
|
117
|
+
--ff-otp-shadow: inset 0 1px 4px -1px rgba(0, 0, 0, 0.06);
|
|
118
|
+
--ff-otp-shadow-focus: inset 0 1px 4px -1px rgba(0, 0, 0, 0.06), 0 0 0 3px rgba(var(--ff-color-primary-rgb), 0.12);
|
|
119
|
+
--ff-otp-shadow-focus-error:
|
|
120
|
+
inset 0 1px 4px -1px rgba(0, 0, 0, 0.06), 0 0 0 3px rgba(var(--ff-color-danger-rgb), 0.12);
|
|
77
121
|
}
|
|
78
122
|
|
|
79
123
|
[data-fanfare-variant="clean"] {
|
|
80
124
|
--ff-variant: "clean";
|
|
125
|
+
--ff-numeral-advance: 0.58;
|
|
126
|
+
--ff-countdown-cell-gap: 0.125rem;
|
|
127
|
+
--ff-countdown-inline-weight: 500;
|
|
128
|
+
--ff-countdown-inline-tracking: 0.02em;
|
|
129
|
+
--ff-countdown-reserve-gap: 2rem;
|
|
130
|
+
--ff-countdown-numeral-font: var(--ff-font-mono);
|
|
81
131
|
--ff-color-background: #ffffff;
|
|
82
132
|
--ff-color-foreground: #0a0a0a;
|
|
83
133
|
--ff-color-muted: #737373;
|
|
84
134
|
--ff-color-muted-foreground: #737373;
|
|
85
135
|
--ff-color-border: #e8e8e8;
|
|
136
|
+
--ff-card-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), 0 8px 24px rgba(0, 0, 0, 0.06);
|
|
137
|
+
--ff-card-shadow-elevated: var(--ff-card-shadow);
|
|
138
|
+
--ff-progress-bar-shadow: 0 0 #0000;
|
|
86
139
|
--ff-font-sans: "Figtree", system-ui, sans-serif;
|
|
87
140
|
--ff-font-heading: "Figtree", system-ui, sans-serif;
|
|
88
141
|
--ff-font-mono: "Inconsolata", "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
@@ -98,6 +151,154 @@
|
|
|
98
151
|
font-family: var(--ff-font-sans);
|
|
99
152
|
color: var(--ff-color-foreground);
|
|
100
153
|
background-color: var(--ff-color-surface);
|
|
154
|
+
--ff-widget-max-width: 24rem;
|
|
155
|
+
|
|
156
|
+
/* The advance width of one digit as a fraction of the font size, for the
|
|
157
|
+
* variant's numeral typeface. Fitting a fixed-width glyph run to a
|
|
158
|
+
* container needs it; over-stating it only shrinks the numerals. */
|
|
159
|
+
--ff-numeral-advance: 0.65;
|
|
160
|
+
|
|
161
|
+
/* Display text that fits its container: a `cqi`-driven scale between a
|
|
162
|
+
* legible floor and a ceiling the design allows. */
|
|
163
|
+
--ff-stat-min: 1.5rem;
|
|
164
|
+
--ff-stat-scale: 9cqi;
|
|
165
|
+
--ff-stat-max: 2.25rem;
|
|
166
|
+
--ff-stat-weight: 700;
|
|
167
|
+
--ff-title-min: 1rem;
|
|
168
|
+
--ff-title-scale: 6cqi;
|
|
169
|
+
--ff-title-max: 1.25rem;
|
|
170
|
+
--ff-title-weight: 600;
|
|
171
|
+
|
|
172
|
+
/* Countdown: geometry, colour, label placement, separator and inline type,
|
|
173
|
+
* overridden per variant in the blocks above. */
|
|
174
|
+
--ff-countdown-cell-gap: 0.75rem;
|
|
175
|
+
--ff-countdown-cell-padding: 0;
|
|
176
|
+
--ff-countdown-cell-radius: 0;
|
|
177
|
+
--ff-countdown-cell-border-width: 0px;
|
|
178
|
+
--ff-countdown-cell-bg: transparent;
|
|
179
|
+
--ff-countdown-cell-shadow: none;
|
|
180
|
+
--ff-countdown-cell-shadow-hover: var(--ff-countdown-cell-shadow);
|
|
181
|
+
--ff-countdown-numeral-color: var(--ff-color-foreground);
|
|
182
|
+
--ff-countdown-numeral-font: var(--ff-font-heading);
|
|
183
|
+
--ff-countdown-label-color: var(--ff-color-muted-foreground);
|
|
184
|
+
--ff-countdown-label-size: 0.625rem;
|
|
185
|
+
--ff-countdown-label-order: 1;
|
|
186
|
+
--ff-countdown-separator-glyph: "/";
|
|
187
|
+
--ff-countdown-separator-display: inline;
|
|
188
|
+
--ff-countdown-separator-color: var(--ff-color-muted-foreground);
|
|
189
|
+
--ff-countdown-inline-size: inherit;
|
|
190
|
+
--ff-countdown-inline-weight: 600;
|
|
191
|
+
--ff-countdown-inline-tracking: 0;
|
|
192
|
+
|
|
193
|
+
/* Numeral fitting: the bounds the fitted numeral size is clamped to, and
|
|
194
|
+
* the horizontal space the numerals cannot have — the panel's own padding
|
|
195
|
+
* plus, per variant, each cell's padding and border and each gap's width
|
|
196
|
+
* and separator glyph. Rounded up: over-reserving shrinks the numerals,
|
|
197
|
+
* under-reserving overflows the widget. */
|
|
198
|
+
--ff-countdown-numeral-min: 1.25rem;
|
|
199
|
+
--ff-countdown-numeral-max: 2.5rem;
|
|
200
|
+
--ff-countdown-reserve-outer: 2.5rem;
|
|
201
|
+
--ff-countdown-reserve-cell: 0rem;
|
|
202
|
+
--ff-countdown-reserve-gap: 3rem;
|
|
203
|
+
|
|
204
|
+
/* Countdown urgency: the colours a warning or critical cell takes. */
|
|
205
|
+
--ff-countdown-tone-warning-color: var(--ff-color-warning);
|
|
206
|
+
--ff-countdown-tone-critical-color: var(--ff-color-danger);
|
|
207
|
+
--ff-countdown-tone-critical-bg: transparent;
|
|
208
|
+
--ff-countdown-tone-critical-fg: inherit;
|
|
209
|
+
|
|
210
|
+
/* Panel geometry: the padding around a panel's stack and the gap between
|
|
211
|
+
* the rows of that stack and of a panel body. */
|
|
212
|
+
--ff-panel-gap: 1.25rem;
|
|
213
|
+
--ff-panel-padding: 1.25rem;
|
|
214
|
+
|
|
215
|
+
/* Surface elevation, one token per surface and per interactive state it
|
|
216
|
+
* distinguishes. A variant that raises a surface overrides these. */
|
|
217
|
+
--ff-card-shadow: none;
|
|
218
|
+
--ff-card-shadow-hover: var(--ff-card-shadow);
|
|
219
|
+
|
|
220
|
+
/* The `elevated` card is raised above its aesthetic's resting surface, so it
|
|
221
|
+
* carries its own pair rather than reading the resting one. The default is
|
|
222
|
+
* the elevation the variant-less card has always drawn. */
|
|
223
|
+
--ff-card-shadow-elevated: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
224
|
+
--ff-card-shadow-elevated-hover: var(--ff-card-shadow-elevated);
|
|
225
|
+
--ff-progress-shadow: none;
|
|
226
|
+
--ff-progress-bar-shadow: none;
|
|
227
|
+
--ff-loading-shadow: none;
|
|
228
|
+
|
|
229
|
+
/* Surfaces that also carry a focus ring compose their shadow with it
|
|
230
|
+
* through Tailwind's own shadow utility, so the empty value here is the
|
|
231
|
+
* transparent shadow that composes rather than the `none` that would
|
|
232
|
+
* invalidate the whole list — and the ring with it. */
|
|
233
|
+
--ff-button-shadow: 0 0 #0000;
|
|
234
|
+
--ff-button-shadow-hover: var(--ff-button-shadow);
|
|
235
|
+
--ff-button-shadow-active: var(--ff-button-shadow);
|
|
236
|
+
--ff-button-shadow-primary: var(--ff-button-shadow);
|
|
237
|
+
--ff-input-shadow: 0 0 #0000;
|
|
238
|
+
--ff-input-shadow-focus: var(--ff-input-shadow);
|
|
239
|
+
--ff-otp-shadow: 0 0 #0000;
|
|
240
|
+
--ff-otp-shadow-focus: var(--ff-otp-shadow);
|
|
241
|
+
--ff-otp-shadow-focus-error: var(--ff-otp-shadow-focus);
|
|
242
|
+
|
|
243
|
+
/* Product selector row: the box a row draws and the square its thumbnail
|
|
244
|
+
* occupies inside it. */
|
|
245
|
+
--ff-row-gap: 0.75rem;
|
|
246
|
+
--ff-row-padding: 0.75rem;
|
|
247
|
+
--ff-row-radius: 0.375rem;
|
|
248
|
+
--ff-row-thumb-size: 2.75rem;
|
|
249
|
+
--ff-row-thumb-size-narrow: 2.5rem;
|
|
250
|
+
|
|
251
|
+
/* Ledger rows: the rule drawn between them, and the colour each tone
|
|
252
|
+
* carries on the marker glyph and the value. */
|
|
253
|
+
--ff-ledger-row-padding: 0.375rem;
|
|
254
|
+
--ff-ledger-border-color: color-mix(in oklab, var(--ff-color-border) 40%, transparent);
|
|
255
|
+
--ff-ledger-tone-default: var(--ff-color-foreground);
|
|
256
|
+
--ff-ledger-tone-muted: var(--ff-color-muted);
|
|
257
|
+
--ff-ledger-tone-live: var(--ff-color-primary);
|
|
258
|
+
--ff-ledger-tone-danger: var(--ff-color-danger);
|
|
259
|
+
--ff-ledger-tone-success: var(--ff-color-success);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Hero is the same countdown at a larger scale: a taller numeral ceiling, a
|
|
264
|
+
* wider cell gap, and a gap reserve that counts that gap either side of the
|
|
265
|
+
* larger separator glyph. The variants that draw no separator reserve the
|
|
266
|
+
* gap alone.
|
|
267
|
+
*/
|
|
268
|
+
.fanfare-themed [data-slot="countdown"][data-size="hero"] {
|
|
269
|
+
--ff-countdown-numeral-max: 3.75rem;
|
|
270
|
+
--ff-countdown-cell-gap: 1rem;
|
|
271
|
+
--ff-countdown-reserve-gap: 3.75rem;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.fanfare-themed[data-fanfare-variant="retro"] [data-slot="countdown"][data-size="hero"],
|
|
275
|
+
.fanfare-themed[data-fanfare-variant="rounded"] [data-slot="countdown"][data-size="hero"] {
|
|
276
|
+
--ff-countdown-reserve-gap: 1rem;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Four unit labels plus three separators need more width than the widget
|
|
281
|
+
* has, so a countdown showing four cells drops its separators at every size
|
|
282
|
+
* and reserves the cell gap alone between cells.
|
|
283
|
+
*/
|
|
284
|
+
.fanfare-themed [data-slot="countdown"][data-units="4"] {
|
|
285
|
+
--ff-countdown-separator-display: none;
|
|
286
|
+
--ff-countdown-reserve-gap: var(--ff-countdown-cell-gap);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* The boxed variants' cell padding alone would leave four cells no room for
|
|
291
|
+
* numerals at the narrowest widget width the cells layout renders at, so a
|
|
292
|
+
* four-cell row narrows each cell's side padding; the cell reserve follows.
|
|
293
|
+
*/
|
|
294
|
+
.fanfare-themed[data-fanfare-variant="rounded"] [data-slot="countdown"][data-units="4"] {
|
|
295
|
+
--ff-countdown-cell-padding: 0.75rem 0.5rem;
|
|
296
|
+
--ff-countdown-reserve-cell: 1.125rem;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.fanfare-themed[data-fanfare-variant="retro"] [data-slot="countdown"][data-units="4"] {
|
|
300
|
+
--ff-countdown-cell-padding: 0.5rem 0.5rem;
|
|
301
|
+
--ff-countdown-reserve-cell: 1.5rem;
|
|
101
302
|
}
|
|
102
303
|
|
|
103
304
|
/**
|
|
@@ -141,3 +342,250 @@
|
|
|
141
342
|
}
|
|
142
343
|
}
|
|
143
344
|
}
|
|
345
|
+
|
|
346
|
+
@layer components {
|
|
347
|
+
/**
|
|
348
|
+
* Countdown token consumers. The layer keeps a one-off Tailwind utility
|
|
349
|
+
* winning over these rules, and the `.fanfare-themed` prefix keeps them from
|
|
350
|
+
* reaching a host page's own markup once the stylesheet is imported.
|
|
351
|
+
*/
|
|
352
|
+
/**
|
|
353
|
+
* Cells are a two-row grid: every unit spans both rows through a subgrid, so
|
|
354
|
+
* the numerals share the first row and each separator centres on that row
|
|
355
|
+
* whatever size the numerals fit to.
|
|
356
|
+
*/
|
|
357
|
+
.fanfare-themed [data-slot="countdown-cells"] {
|
|
358
|
+
column-gap: var(--ff-countdown-cell-gap);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.fanfare-themed [data-slot="countdown-unit"] {
|
|
362
|
+
padding: var(--ff-countdown-cell-padding);
|
|
363
|
+
border-radius: var(--ff-countdown-cell-radius);
|
|
364
|
+
border-width: var(--ff-countdown-cell-border-width);
|
|
365
|
+
border-style: solid;
|
|
366
|
+
background-color: var(--ff-countdown-cell-bg);
|
|
367
|
+
box-shadow: var(--ff-countdown-cell-shadow);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* Hover elevation is gated on a real pointer, so a tap does not leave a
|
|
371
|
+
* cell raised. */
|
|
372
|
+
@media (hover: hover) {
|
|
373
|
+
.fanfare-themed [data-slot="countdown-unit"]:hover {
|
|
374
|
+
box-shadow: var(--ff-countdown-cell-shadow-hover);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* Per-digit boxes inherit the numeral face from their unit. */
|
|
379
|
+
.fanfare-themed [data-slot="countdown-unit-digits"] {
|
|
380
|
+
color: var(--ff-countdown-numeral-color);
|
|
381
|
+
font-family: var(--ff-countdown-numeral-font);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Per-unit numerals size themselves to the widget: the container's inline
|
|
386
|
+
* size less the reserved chrome, divided across every digit on screen
|
|
387
|
+
* (`--ff-countdown-digits`, across `--ff-countdown-cells` cells, at the
|
|
388
|
+
* variant's advance ratio). The per-digit granularity is excluded — its cells are fixed boxes
|
|
389
|
+
* whose numerals are sized by utility to match.
|
|
390
|
+
*
|
|
391
|
+
* `cqi` cannot name a container, so it binds to the nearest one. Every
|
|
392
|
+
* countdown the SDK renders sits under the widget root and outside
|
|
393
|
+
* `ProductSelector`'s own container, so this reads the widget's width.
|
|
394
|
+
*/
|
|
395
|
+
.fanfare-themed [data-cells="unit"] [data-slot="countdown-unit-digits"] {
|
|
396
|
+
font-size: clamp(
|
|
397
|
+
var(--ff-countdown-numeral-min),
|
|
398
|
+
calc(
|
|
399
|
+
(
|
|
400
|
+
100cqi - var(--ff-countdown-reserve-outer) - var(--ff-countdown-cells) * var(--ff-countdown-reserve-cell) -
|
|
401
|
+
(var(--ff-countdown-cells) - 1) * var(--ff-countdown-reserve-gap)
|
|
402
|
+
) /
|
|
403
|
+
(var(--ff-countdown-digits) * var(--ff-numeral-advance))
|
|
404
|
+
),
|
|
405
|
+
var(--ff-countdown-numeral-max)
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.fanfare-themed [data-slot="countdown-unit-label"] {
|
|
410
|
+
order: var(--ff-countdown-label-order);
|
|
411
|
+
font-size: var(--ff-countdown-label-size);
|
|
412
|
+
color: var(--ff-countdown-label-color);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.fanfare-themed [data-slot="countdown-separator"] {
|
|
416
|
+
display: var(--ff-countdown-separator-display);
|
|
417
|
+
color: var(--ff-countdown-separator-color);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.fanfare-themed [data-slot="countdown-separator"]::before {
|
|
421
|
+
content: var(--ff-countdown-separator-glyph);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.fanfare-themed [data-layout="inline"][data-slot="countdown"],
|
|
425
|
+
.fanfare-themed [data-slot="countdown-inline"] {
|
|
426
|
+
font-size: var(--ff-countdown-inline-size);
|
|
427
|
+
font-weight: var(--ff-countdown-inline-weight);
|
|
428
|
+
letter-spacing: var(--ff-countdown-inline-tracking);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Both layouts publish the resolved tone on their root: the cells layout
|
|
433
|
+
* colours each unit's numerals, the inline layout colours the single line,
|
|
434
|
+
* and the cells layout's narrow-container inline fallback picks up the same
|
|
435
|
+
* rule.
|
|
436
|
+
*/
|
|
437
|
+
.fanfare-themed [data-slot="countdown-unit"][data-tone="warning"] [data-slot="countdown-unit-digits"],
|
|
438
|
+
.fanfare-themed [data-slot="countdown"][data-tone="warning"] [data-slot="countdown-inline"] {
|
|
439
|
+
color: var(--ff-countdown-tone-warning-color);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.fanfare-themed [data-slot="countdown-unit"][data-tone="critical"] [data-slot="countdown-unit-digits"],
|
|
443
|
+
.fanfare-themed [data-slot="countdown"][data-tone="critical"] [data-slot="countdown-inline"] {
|
|
444
|
+
color: var(--ff-countdown-tone-critical-color);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Retro's urgency accent is part of that variant's aesthetic rather than the
|
|
449
|
+
* global danger semantic, so the filled cell is scoped to the retro variant
|
|
450
|
+
* instead of moving `--ff-color-danger`. Other variants keep the cell surface
|
|
451
|
+
* their geometry tokens give them and carry urgency in the numeral colour
|
|
452
|
+
* alone.
|
|
453
|
+
*/
|
|
454
|
+
.fanfare-themed[data-fanfare-variant="retro"] [data-slot="countdown-unit"][data-tone="critical"] {
|
|
455
|
+
background-color: var(--ff-countdown-tone-critical-bg);
|
|
456
|
+
color: var(--ff-countdown-tone-critical-fg);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* The global critical rule sets the numeral colour directly, so the filled
|
|
461
|
+
* cell's foreground has to be restated on the numerals and label rather than
|
|
462
|
+
* relied on through inheritance.
|
|
463
|
+
*/
|
|
464
|
+
.fanfare-themed[data-fanfare-variant="retro"]
|
|
465
|
+
[data-slot="countdown-unit"][data-tone="critical"]
|
|
466
|
+
[data-slot="countdown-unit-digits"],
|
|
467
|
+
.fanfare-themed[data-fanfare-variant="retro"]
|
|
468
|
+
[data-slot="countdown-unit"][data-tone="critical"]
|
|
469
|
+
[data-slot="countdown-unit-label"] {
|
|
470
|
+
color: var(--ff-countdown-tone-critical-fg);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Display text that is prose rather than a fixed glyph run: it scales with
|
|
475
|
+
* the widget between a legible floor and the design's ceiling, so a narrow
|
|
476
|
+
* embed shrinks the words instead of wrapping or clipping them.
|
|
477
|
+
*/
|
|
478
|
+
.fanfare-themed [data-slot="stat-value"] {
|
|
479
|
+
font-size: clamp(var(--ff-stat-min), var(--ff-stat-scale), var(--ff-stat-max));
|
|
480
|
+
font-weight: var(--ff-stat-weight);
|
|
481
|
+
line-height: 1.2;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.fanfare-themed [data-slot="panel-title"] {
|
|
485
|
+
font-size: clamp(var(--ff-title-min), var(--ff-title-scale), var(--ff-title-max));
|
|
486
|
+
font-weight: var(--ff-title-weight);
|
|
487
|
+
line-height: 1.4;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.fanfare-themed [data-slot="panel-body"],
|
|
491
|
+
.fanfare-themed [data-slot="panel-stack"] {
|
|
492
|
+
gap: var(--ff-panel-gap);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.fanfare-themed [data-slot="panel-stack"] {
|
|
496
|
+
padding: var(--ff-panel-padding);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Every Card carries `data-fanfare-card` whatever `data-slot` its consumer
|
|
501
|
+
* publishes on it, so the surface rules key on that marker rather than on a
|
|
502
|
+
* slot name a panel is free to replace.
|
|
503
|
+
*/
|
|
504
|
+
.fanfare-themed [data-fanfare-card] {
|
|
505
|
+
box-shadow: var(--ff-card-shadow);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* A raised card publishes `data-elevated`, so its elevation is a token the
|
|
510
|
+
* variant reaches rather than a utility that outranks every variant's
|
|
511
|
+
* aesthetic.
|
|
512
|
+
*/
|
|
513
|
+
.fanfare-themed [data-fanfare-card][data-elevated] {
|
|
514
|
+
box-shadow: var(--ff-card-shadow-elevated);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
@media (hover: hover) {
|
|
518
|
+
.fanfare-themed [data-fanfare-card]:hover {
|
|
519
|
+
box-shadow: var(--ff-card-shadow-hover);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.fanfare-themed [data-fanfare-card][data-elevated]:hover {
|
|
523
|
+
box-shadow: var(--ff-card-shadow-elevated-hover);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.fanfare-themed [data-slot="progress-track"] {
|
|
528
|
+
box-shadow: var(--ff-progress-shadow);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.fanfare-themed [data-slot="progress-bar"] {
|
|
532
|
+
box-shadow: var(--ff-progress-bar-shadow);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.fanfare-themed [data-slot="loading-spinner"] {
|
|
536
|
+
box-shadow: var(--ff-loading-shadow);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.fanfare-themed [data-slot="product-selector-row"] {
|
|
540
|
+
gap: var(--ff-row-gap);
|
|
541
|
+
padding: var(--ff-row-padding);
|
|
542
|
+
border-radius: var(--ff-row-radius);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.fanfare-themed [data-slot="product-thumb"] {
|
|
546
|
+
width: var(--ff-row-thumb-size);
|
|
547
|
+
height: var(--ff-row-thumb-size);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* The thumbnail takes its narrow side length once the picker's own container
|
|
552
|
+
* is too tight for the wide one. The query is unnamed so it binds to that
|
|
553
|
+
* container — the nearest one — rather than to the widget, which a picker
|
|
554
|
+
* rendered on its own need not sit inside.
|
|
555
|
+
*/
|
|
556
|
+
@container not (min-width: 400px) {
|
|
557
|
+
.fanfare-themed [data-slot="product-thumb"] {
|
|
558
|
+
width: var(--ff-row-thumb-size-narrow);
|
|
559
|
+
height: var(--ff-row-thumb-size-narrow);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.fanfare-themed [data-slot="ledger-row"] {
|
|
564
|
+
padding-block: var(--ff-ledger-row-padding);
|
|
565
|
+
border-bottom-color: var(--ff-ledger-border-color);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Tone is published on the row and inherited by the marker glyph and the
|
|
570
|
+
* value; the label states its own muted colour and is unaffected.
|
|
571
|
+
*/
|
|
572
|
+
.fanfare-themed [data-slot="ledger-row"][data-tone="default"] {
|
|
573
|
+
color: var(--ff-ledger-tone-default);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.fanfare-themed [data-slot="ledger-row"][data-tone="muted"] {
|
|
577
|
+
color: var(--ff-ledger-tone-muted);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.fanfare-themed [data-slot="ledger-row"][data-tone="live"] {
|
|
581
|
+
color: var(--ff-ledger-tone-live);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.fanfare-themed [data-slot="ledger-row"][data-tone="danger"] {
|
|
585
|
+
color: var(--ff-ledger-tone-danger);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.fanfare-themed [data-slot="ledger-row"][data-tone="success"] {
|
|
589
|
+
color: var(--ff-ledger-tone-success);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { WidgetVariant } from '@fanfare-io/fanfare-sdk-core/theme';
|
|
2
|
+
/** The catalogue categories, each with its own fallback stack. */
|
|
3
|
+
export type FontCategory = "Display" | "Handwriting" | "Monospace" | "Sans Serif" | "Serif";
|
|
4
|
+
export interface FontCatalogueEntry {
|
|
5
|
+
category: FontCategory;
|
|
6
|
+
/** The weights the family publishes; requesting one it lacks fails the whole font request. */
|
|
7
|
+
weights: number[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Generic stacks a browser can render without a network request, so a family that fails to load
|
|
11
|
+
* degrades to a face of the same flavour rather than to the default serif.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CATEGORY_FALLBACKS: Record<FontCategory, string>;
|
|
14
|
+
/**
|
|
15
|
+
* CSS generic families and CSS-wide keywords. A font value that is one of these names no family at
|
|
16
|
+
* all, so it is neither fetchable nor quotable: quoting it would make the browser look for a family
|
|
17
|
+
* literally called `serif`. Kept lowercase because family names are case-insensitive in CSS.
|
|
18
|
+
*/
|
|
19
|
+
export declare const GENERIC_FONT_KEYWORDS: ReadonlySet<string>;
|
|
20
|
+
/** The families the admin font picker offers, in the picker's own order. */
|
|
21
|
+
export declare const FONT_CATALOGUE: Record<string, FontCatalogueEntry>;
|
|
22
|
+
/** Whether a family resolves without a network request. Family names are case-insensitive in CSS. */
|
|
23
|
+
export declare const isSystemFont: (family: string) => boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The catalogue's own spelling of a family. CSS matches family names case-insensitively, but the
|
|
26
|
+
* Google Fonts css2 endpoint does not — it rejects `figtree` outright — so a host's casing is
|
|
27
|
+
* resolved to the catalogue's before anything is requested.
|
|
28
|
+
*/
|
|
29
|
+
export declare const catalogueFamilyName: (family: string) => string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The Google Fonts stylesheet for a family, asking for exactly the weights it publishes — the
|
|
32
|
+
* css2 endpoint rejects the whole request when it names a weight the family lacks, and several
|
|
33
|
+
* curated families publish fewer than the usual 400/500/600/700. A family outside the catalogue
|
|
34
|
+
* has no known weight list, so it is requested without a weight axis at all.
|
|
35
|
+
*/
|
|
36
|
+
export declare const googleFontStylesheetHref: (family: string) => string;
|
|
37
|
+
/** The faces a variant's own token block names: its text face, and its mono face where it has one. */
|
|
38
|
+
export interface VariantFamilies {
|
|
39
|
+
body: string;
|
|
40
|
+
mono?: string;
|
|
41
|
+
}
|
|
42
|
+
/** The families each variant's own token block names, used when a theme names none of its own. */
|
|
43
|
+
export declare const VARIANT_DEFAULT_FAMILIES: Record<WidgetVariant, VariantFamilies>;
|
|
44
|
+
/**
|
|
45
|
+
* The faces to fetch for a variant. A variant name the build does not know still reaches here — a
|
|
46
|
+
* theme served by a newer admin, or a hand-written element attribute — and takes the default
|
|
47
|
+
* variant's faces, which are the ones its token block falls back to as well.
|
|
48
|
+
*/
|
|
49
|
+
export declare const variantFamilies: (variant: string | undefined) => VariantFamilies;
|
|
50
|
+
/**
|
|
51
|
+
* Requests a family's stylesheet at most once per page. The link is inserted for print only and
|
|
52
|
+
* promoted to every medium once it has loaded, so a slow or unreachable font origin never holds
|
|
53
|
+
* first paint. A request that fails leaves nothing behind and is never retried — the theme's
|
|
54
|
+
* fallback stack carries the text, and a second attempt would spend the same latency again.
|
|
55
|
+
*/
|
|
56
|
+
export declare function ensureFontStylesheet(family: string): void;
|
|
57
|
+
/** Test-only seam: forgets which families were requested. Never exported from the package entry. */
|
|
58
|
+
export declare function resetFontLoaderCache(): void;
|
|
@@ -2,8 +2,16 @@ import { BrandTheme } from '@fanfare-io/fanfare-sdk-core/theme';
|
|
|
2
2
|
import { ResolvedTheme, WidgetVariant } from './theme.types';
|
|
3
3
|
/**
|
|
4
4
|
* Context value for theme state.
|
|
5
|
+
*
|
|
6
|
+
* `theme` is the merged value a component renders with, every field filled from the SDK defaults.
|
|
7
|
+
* `overrides` answers the different question of what the host actually asked for: the raw fields
|
|
8
|
+
* the providers above this point set, accumulated but never merged with defaults, and `{}` where no
|
|
9
|
+
* provider set anything. A merged value cannot answer that — a host naming the default family is
|
|
10
|
+
* indistinguishable there from a host naming nothing.
|
|
5
11
|
*/
|
|
6
12
|
export interface ThemeContextValue extends ResolvedTheme {
|
|
13
|
+
/** The raw fields providers up the tree explicitly set, without defaults */
|
|
14
|
+
overrides: BrandTheme;
|
|
7
15
|
/** Whether this is the root theme provider */
|
|
8
16
|
isRoot: boolean;
|
|
9
17
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fanfare-io/fanfare-sdk-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "React adapter for Fanfare SDK",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"nanostores": "^1.1.0",
|
|
44
44
|
"tailwind-merge": "^2.6.0",
|
|
45
45
|
"valibot": "^1.1.0",
|
|
46
|
-
"@fanfare-io/fanfare-sdk-core": "0.
|
|
47
|
-
"@fanfare-io/fanfare-sdk-i18n": "0.
|
|
46
|
+
"@fanfare-io/fanfare-sdk-core": "0.22.0",
|
|
47
|
+
"@fanfare-io/fanfare-sdk-i18n": "0.22.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@iconify-json/heroicons": "^1.2.3",
|
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
"@size-limit/preset-small-lib": "^11.1.8",
|
|
56
56
|
"@storybook/addon-a11y": "^10.4.0",
|
|
57
57
|
"@storybook/addon-docs": "^10.4.0",
|
|
58
|
-
"@storybook/addon-themes": "^10.4.0",
|
|
59
58
|
"@storybook/addon-vitest": "~10.2.16",
|
|
60
59
|
"@storybook/react-vite": "^10.4.0",
|
|
61
60
|
"@stripe/stripe-js": "^7.3.1",
|
|
@@ -91,8 +90,8 @@
|
|
|
91
90
|
"vite-plugin-dts": "^4.5.4",
|
|
92
91
|
"vite-tsconfig-paths": "^5.1.4",
|
|
93
92
|
"vitest": "^5.0.0",
|
|
94
|
-
"@fanfare-io/fanfare-sdk-core": "0.
|
|
95
|
-
"@fanfare-io/fanfare-sdk-solid": "0.
|
|
93
|
+
"@fanfare-io/fanfare-sdk-core": "0.22.0",
|
|
94
|
+
"@fanfare-io/fanfare-sdk-solid": "0.22.0"
|
|
96
95
|
},
|
|
97
96
|
"sideEffects": [
|
|
98
97
|
"**/*.css"
|
|
@@ -110,7 +109,7 @@
|
|
|
110
109
|
{
|
|
111
110
|
"name": "React adapter",
|
|
112
111
|
"path": "dist/index.js",
|
|
113
|
-
"limit": "
|
|
112
|
+
"limit": "70 KB",
|
|
114
113
|
"ignore": [
|
|
115
114
|
"react",
|
|
116
115
|
"react-dom",
|