@metrifox/react-sdk 0.0.20-beta.4 → 0.0.20-beta.6

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 CHANGED
@@ -4,6 +4,21 @@ A fully-configurable **React SDK** providing ready-to-use widgets for SaaS and b
4
4
 
5
5
  ---
6
6
 
7
+ ## ⚠️ Version notice – breaking changes
8
+
9
+ **If you are upgrading from an earlier version of the SDK, please read this.**
10
+
11
+ This release introduces a **new theme structure** for both **Customer Portal** and **Pricing Table**. The theme API has been reorganized and is **not backward compatible** with previous versions.
12
+
13
+ - **Theme is now a single object** passed via `metrifoxInit({ theme })` with two keys: `customerPortal` and `pricingTable`. The previous top-level `pricingTableTheme` option is **deprecated**; use `theme.pricingTable` instead.
14
+ - **Customer Portal theme** uses a new grouped shape: `general`, `tabs`, `sections`, `buttons`, `lineItems`, `tables`, `modals`, and `plans`. Property names and nesting have changed from older versions.
15
+ - **Pricing Table theme** now strictly follows the same nested structure as Customer Portal: all plan-related keys must be under `plans` (e.g. `plans.planCards`, `plans.planToggle`). Top-level plan keys are no longer supported.
16
+ - **Both widgets** accept an optional `theme` prop per instance to override or extend the global theme from `metrifoxInit`.
17
+
18
+ If you were using custom themes before, you will need to **migrate your theme objects** to the new structure. Omit any keys you don't need; the SDK merges your values with defaults. See the [Styling](#styling) section below for the full theme shapes and examples.
19
+
20
+ ---
21
+
7
22
  ## Installation
8
23
 
9
24
  ```bash
@@ -59,12 +74,13 @@ export default function MyPortalPage() {
59
74
  { key: "plan", component: MyCustomPlan, props: { foo: "bar" } },
60
75
  { key: "billingHistory", hidden: true },
61
76
  ]}
77
+ theme={{ general: { linkColor: "#2563eb" } }}
62
78
  />
63
79
  )
64
80
  }
65
81
  ```
66
82
 
67
- ---
83
+ Optional **`theme`** prop: pass a `CustomerPortalTheme` object to override or extend the global theme from `metrifoxInit` for this instance only.
68
84
 
69
85
  #### Section Configuration
70
86
 
@@ -115,7 +131,13 @@ Displays subscription plans and one-time purchases in a configurable pricing tab
115
131
  import { PricingTable } from "@metrifox/react-sdk"
116
132
 
117
133
  export default function PricingPage() {
118
- return <PricingTable checkoutUsername="your-checkout-username" productKey="your-product-key" />
134
+ return (
135
+ <PricingTable
136
+ checkoutUsername="your-checkout-username"
137
+ productKey="your-product-key"
138
+ theme={{ plans: { planCards: { background: "#ffffff" } } }}
139
+ />
140
+ )
119
141
  }
120
142
  ```
121
143
 
@@ -125,19 +147,20 @@ export default function PricingPage() {
125
147
 
126
148
  The props control how the pricing table is configured.
127
149
 
128
- | Property | Type | Required | Default | Description |
129
- | --------------------- | --------- | -------- | ------- | -------------------------------------------------------------------------------- |
130
- | `checkoutUsername` | `string` | Yes | — | Unique username used for checkout. This can be found in **Settings → Checkout**. |
131
- | `productKey` | `string` | Yes | — | Unique product identifier. This can be found on the product page. |
132
- | `plansOnly` | `boolean` | No | `false` | Controls whether only subscription plans are rendered. |
133
- | `singlePurchasesOnly` | `boolean` | No | `false` | Controls whether only single purchases are rendered. |
134
- | `showTabHeader` | `boolean` | No | `true` | Controls whether the tab header for switching between offerings is rendered. |
150
+ | Property | Type | Required | Default | Description |
151
+ | --------------------- | ------------------ | -------- | ------- | -------------------------------------------------------------------------------- |
152
+ | `checkoutUsername` | `string` | Yes | — | Unique username used for checkout. This can be found in **Settings → Checkout**. |
153
+ | `productKey` | `string` | Yes | — | Unique product identifier. This can be found on the product page. |
154
+ | `plansOnly` | `boolean` | No | `false` | Controls whether only subscription plans are rendered. |
155
+ | `singlePurchasesOnly` | `boolean` | No | `false` | Controls whether only single purchases are rendered. |
156
+ | `showTabHeader` | `boolean` | No | `true` | Controls whether the tab header for switching between offerings is rendered. |
157
+ | `theme` | `PricingTableTheme`| No | — | Optional theme override for this instance (merged with global theme from `metrifoxInit`). |
135
158
 
136
159
  > **Note:** If both `plansOnly` and `singlePurchasesOnly` are `false` or undefined, both plans and single purchases are displayed.
137
160
 
138
161
  ## Styling
139
162
 
140
- Import the SDKs global styles into your app entry (e.g., `src/index.tsx` or `_app.tsx`):
163
+ Import the SDK's global styles into your app entry (e.g., `src/index.tsx` or `_app.tsx`):
141
164
 
142
165
  ```tsx
143
166
  import "@metrifox/react-sdk/dist/styles.css"
@@ -145,274 +168,209 @@ import "@metrifox/react-sdk/dist/styles.css"
145
168
 
146
169
  > This is required for proper styling of all widgets.
147
170
 
148
- #### Widget Styling
149
-
150
- The SDK accepts an optional `theme` object during initialization. This theme is applied to Widgets and UI elements within them to match your brand styles.
171
+ ### Theme configuration (new structure)
151
172
 
152
- Any values not provided will fall back to the default theme.
173
+ Theming is driven by a single `theme` object passed to `metrifoxInit`. It has two top-level keys: `customerPortal` and `pricingTable`. Any value you omit falls back to the SDK default. You can also pass an optional `theme` prop to `<CustomerPortal />` or `<PricingTable />` to override or extend the global theme for that instance.
153
174
 
154
175
  ```ts
176
+ // Passed to metrifoxInit({ theme })
155
177
  theme?: {
156
178
  customerPortal?: CustomerPortalTheme
157
179
  pricingTable?: PricingTableTheme
158
180
  }
159
181
  ```
160
182
 
161
- ```ts
162
- // Customer portal theme (CustomerPortalTheme). All properties are optional.
163
- customerPortal?: {
164
- general?: {
165
- linkColor?: string
166
- backgroundColor?: string
167
- borderRadius?: string
168
- fontFamily?: string
169
- containerPadding?: string
170
- }
183
+ > **Deprecated:** The previous root-level `pricingTableTheme` option is no longer supported. Use `theme.pricingTable` instead.
171
184
 
172
- tabs?: {
173
- tabBackground?: string
174
- tabBorderColor?: string
175
- activeTabBackground?: string
176
- activeTabTextColor?: string
177
- inactiveTabTextColor?: string
178
- }
185
+ ---
179
186
 
180
- sections?: {
181
- background?: string
182
- padding?: string
183
- borderColor?: string
184
- borderRadius?: string
185
- emptyTextColor?: string
186
- usage?: { barColor?: string; trackColor?: string }
187
- content?: { background?: string; padding?: string; borderColor?: string; borderRadius?: string }
188
- summary?: { background?: string; padding?: string; borderColor?: string; borderRadius?: string }
189
- header?: { fontSize?: string; fontWeight?: string; color?: string }
190
- label?: { fontSize?: string; fontWeight?: string; color?: string }
191
- value?: { fontSize?: string; fontWeight?: string; color?: string }
192
- }
187
+ ### Customer Portal theme (`CustomerPortalTheme`)
193
188
 
194
- buttons?: {
195
- primary?: {
196
- backgroundColor?: string
197
- border?: { color?: string; width?: string; radius?: string }
198
- typography?: { fontSize?: string; fontWeight?: string; color?: string }
199
- }
200
- secondary?: {
201
- backgroundColor?: string
202
- border?: { color?: string; width?: string; radius?: string }
203
- typography?: { fontSize?: string; fontWeight?: string; color?: string }
204
- }
205
- }
189
+ All properties are optional. The shape is grouped by area of the UI:
206
190
 
207
- lineItems?: {
208
- parentRow?: {
209
- background?: string
210
- borderColor?: string
211
- typography?: { label?: { color?: string }; quantity?: { color?: string } }
212
- }
213
- childRow?: {
214
- background?: string
215
- borderColor?: string
216
- typography?: { label?: { color?: string }; quantity?: { color?: string } }
217
- }
218
- }
191
+ | Group | Description |
192
+ | ----- | ----------- |
193
+ | `general` | Page-level: link color, background, border radius, font family, container padding |
194
+ | `tabs` | Tab bar (e.g. Wallet balance tabs): background, border, active/inactive states |
195
+ | `sections` | Section cards: background, padding, borders, content/summaryBalance sub-styles, header/label/value typography, usage bars, empty text |
196
+ | `buttons` | Primary and secondary buttons: background, border (color/width/radius), typography |
197
+ | `lineItems` | Subscription line items: parentRow/childRow background, border, typography (label/quantity) |
198
+ | `tables` | Tables (e.g. billing history): header/row colors, border, cell padding, expand icon, typography |
199
+ | `modals` | Modal overlay, background, border, close button; header/title/description typography; footer primary/secondary buttons |
200
+ | `plans` | Plan cards when shown in portal: currentPlanCard, planCards (border as `{ color, width, radius }`), planFeatures, planButton, planToggle, planTags |
219
201
 
220
- tables?: {
221
- headerBackground?: string
222
- headerTextColor?: string
223
- rowBackgroundOdd?: string
224
- rowBackgroundEven?: string
225
- rowTextColor?: string
226
- borderColor?: string
227
- cellPadding?: string
228
- expandIconColor?: string
229
- typography?: {
230
- fontSize?: string
231
- fontWeight?: string
232
- headerFontSize?: string
233
- headerFontWeight?: string
234
- }
235
- }
202
+ **Example – minimal override:**
236
203
 
237
- modals?: {
238
- overlayColor?: string
239
- background?: string
240
- borderColor?: string
241
- borderRadius?: string
242
- closeButtonColor?: string
243
- header?: { fontSize?: string; fontWeight?: string; color?: string }
244
- title?: { fontSize?: string; fontWeight?: string; color?: string }
245
- description?: { fontSize?: string; fontWeight?: string; color?: string }
246
- footer?: {
247
- primary?: { backgroundColor?: string; textColor?: string }
248
- secondary?: { backgroundColor?: string; textColor?: string; borderColor?: string; borderWidth?: string }
249
- }
250
- }
251
-
252
- plans?: {
253
- currentPlanCard?: {
254
- header?: { background?: string; textColor?: string }
255
- gradientColor?: string
256
- }
257
- planCards?: {
258
- background?: string
259
- borderColor?: string
260
- descriptionColor?: string
261
- header?: { background?: string; textColor?: string }
262
- description?: { textColor?: string; textButtonColor?: string }
263
- price?: {
264
- amountColor?: string
265
- primaryTextColor?: string
266
- secondaryTextColor?: string
267
- background?: string
268
- borderColor?: string
269
- }
270
- }
271
- planFeatures?: { textColor?: string; iconColor?: string }
272
- planButton?: { background?: string; textColor?: string }
273
- planToggle?: {
274
- background?: string
275
- activeBackground?: string
276
- activeText?: string
277
- inactiveText?: string
278
- }
279
- planTags?: { freeTrialBackground?: string; freeTrialText?: string }
280
- }
204
+ ```ts
205
+ customerPortal: {
206
+ general: { linkColor: "#2563eb", backgroundColor: "#ffffff" },
207
+ tabs: {
208
+ tabBackground: "#ffffff",
209
+ tabBorderColor: "#e5e7eb",
210
+ activeTabBackground: "#2563eb",
211
+ activeTabTextColor: "#ffffff",
212
+ inactiveTabTextColor: "#6b7280",
213
+ },
214
+ sections: {
215
+ background: "#ffffff",
216
+ content: { background: "#f4f4f5", borderRadius: "8px" },
217
+ header: { fontSize: "16px", fontWeight: "600", color: "#52525b" },
218
+ label: { fontSize: "13px", color: "#71717a" },
219
+ value: { fontSize: "16px", color: "#52525b" },
220
+ },
221
+ buttons: {
222
+ primary: { backgroundColor: "#2563eb", border: { radius: "8px" }, typography: { color: "#ffffff" } },
223
+ secondary: { backgroundColor: "#e4e4e7", typography: { color: "#3f3f46" } },
224
+ },
225
+ plans: {
226
+ planCards: {
227
+ background: "#ffffff",
228
+ border: { color: "#e5e7eb", width: "1px", radius: "8px" },
229
+ header: { background: "#e5e7eb", textColor: "#111827" },
230
+ description: { textColor: "#6b7280", textButtonColor: "#2563eb" },
231
+ price: { amountColor: "#111827", primaryTextColor: "#6b7280", secondaryTextColor: "#9ca3af" },
232
+ },
233
+ planButton: { background: "#2563eb", textColor: "#ffffff" },
234
+ planToggle: { background: "#e5e7eb", activeBackground: "#1f2937", activeText: "#ffffff", inactiveText: "#6b7280" },
235
+ },
281
236
  }
282
237
  ```
283
238
 
284
- ```ts
285
- // Pricing table theme configuration
286
- pricingTableTheme?: {
287
- card?: {
288
- background?: string
289
- borderColor?: string
290
- descriptionColor?: string
291
-
292
- header?: {
293
- background?: string
294
- textColor?: string
295
- }
239
+ ---
296
240
 
297
- description?: {
298
- textColor?: string
299
- textButtonColor?: string
300
- }
241
+ ### Font customization
301
242
 
302
- price?: {
303
- amountColor?: string
304
- primaryTextColor?: string
305
- secondaryTextColor?: string
306
- textButtonColor?: string
307
- background?: string
308
- borderColor?: string
309
- }
310
- }
243
+ The SDK accepts **any font-family string** in your theme. The SDK applies it via CSS, but **your app must load the font** (Google Fonts, @font-face, etc.) before the SDK renders.
311
244
 
312
- button?: {
313
- background?: string
314
- textColor?: string
315
- secondaryBackground?: string
316
- secondaryTextColor?: string
317
- textButtonColor?: string
318
- }
245
+ **How it works:**
319
246
 
320
- featureList?: {
321
- textColor?: string
322
- iconColor?: string
323
- }
247
+ 1. **Load the font in your app** (HTML, CSS, or framework-specific):
324
248
 
325
- tabs?: {
326
- inactiveText?: string
327
- activeText?: string
328
- indicator?: string
329
- borderColor?: string
330
- }
249
+ ```html
250
+ <!-- Option A: Google Fonts in your HTML <head> -->
251
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
252
+ ```
331
253
 
332
- intervalToggle?: {
333
- background?: string
334
- activeBackground?: string
335
- activeText?: string
336
- inactiveText?: string
337
- }
254
+ ```css
255
+ /* Option B: @font-face in your global CSS */
256
+ @font-face {
257
+ font-family: 'MyCustomFont';
258
+ src: url('/fonts/custom.woff2') format('woff2');
259
+ }
260
+ ```
261
+
262
+ 2. **Pass the font-family to the SDK theme:**
338
263
 
339
- currentSubscriptionCard?: {
340
- header?: {
341
- background?: string
342
- textColor?: string
264
+ ```tsx
265
+ metrifoxInit({
266
+ theme: {
267
+ customerPortal: {
268
+ general: {
269
+ // Any font-family string works
270
+ fontFamily: '"Space Grotesk", "Inter", sans-serif'
271
+ // or: 'MyCustomFont, sans-serif'
272
+ // or: 'system-ui, -apple-system, sans-serif'
273
+ }
343
274
  }
344
- gradientColor?: string
345
275
  }
276
+ })
277
+ ```
346
278
 
347
- freeTrialTag?: {
348
- background?: string
349
- textColor?: string
350
- }
279
+ If no `fontFamily` is provided, the SDK inherits the font from the host page. This means the SDK works with whatever font your app already uses — no extra configuration needed.
351
280
 
352
- checkoutBar?: {
353
- background?: string
354
- borderColor?: string
355
- textColor?: string
356
- buttonBackground?: string
357
- buttonTextColor?: string
358
- }
359
- }
360
- ```
281
+ > **Important:** When using Google Fonts or custom fonts, load them in your app **before** the SDK initializes to prevent flash of unstyled text (FOUT).
361
282
 
362
- ###### Example usage
283
+ ---
363
284
 
364
- ```tsx
365
- import { metrifoxInit } from "@metrifox/react-sdk"
285
+ ### Pricing Table theme (`PricingTableTheme`)
366
286
 
367
- const pricingTableTheme = {
368
- pricingTable: {
369
- card: {
287
+ The Pricing Table theme follows the same nested structure as Customer Portal. All plan-related keys must be nested under `plans`.
288
+
289
+ | Key | Description |
290
+ | --- | ----------- |
291
+ | `plans` | Container for all plan-related styling |
292
+ | `plans.currentPlanCard` | Current-plan highlight: header (background, textColor), gradientColor, description, borderRadius |
293
+ | `plans.planCards` | Card style: background, border (`{ color, width, radius }`), header, description (textColor, textButtonColor), price (amountColor, primaryTextColor, secondaryTextColor, textButtonColor, background, borderColor) |
294
+ | `plans.planFeatures` | Feature list: textColor, iconColor |
295
+ | `plans.planButton` | CTA button: background, textColor; optional secondaryBackground, secondaryTextColor, textButtonColor |
296
+ | `plans.planToggle` | Monthly/Yearly toggle: background, activeBackground, activeText, inactiveText |
297
+ | `plans.planTags` | Tags (e.g. free trial): freeTrialBackground, freeTrialText |
298
+ | `tabs` | Tabs (e.g. Plans vs Single purchases): inactiveText, activeText, indicator, borderColor |
299
+ | `checkoutBar` | Bottom checkout bar: background, borderColor, textColor, buttonBackground, buttonTextColor |
300
+
301
+ **Example – minimal override:**
302
+
303
+ ```ts
304
+ pricingTable: {
305
+ plans: {
306
+ planCards: {
370
307
  background: "#ffffff",
371
- price: {
372
- amountColor: "#111827",
373
- textButtonColor: "#2563eb",
374
- },
375
- },
376
- button: {
377
- background: "#2563eb",
378
- },
379
- tabs: {
380
- activeText: "#2563eb",
308
+ border: { color: "#e5e7eb", width: "1px", radius: "8px" },
309
+ header: { background: "#e5e7eb", textColor: "#111827" },
310
+ description: { textColor: "#6b7280", textButtonColor: "#2563eb" },
311
+ price: { amountColor: "#111827", primaryTextColor: "#6b7280", secondaryTextColor: "#9ca3af" },
381
312
  },
313
+ planButton: { background: "#2563eb", textColor: "#ffffff" },
314
+ planToggle: { background: "#e5e7eb", activeBackground: "#1f2937", activeText: "#ffffff", inactiveText: "#6b7280" },
315
+ },
316
+ tabs: { activeText: "#2563eb", indicator: "#2563eb", borderColor: "#9ca3af" },
317
+ checkoutBar: {
318
+ background: "#f9fafb",
319
+ borderColor: "#e5e7eb",
320
+ textColor: "#3f3f46",
321
+ buttonBackground: "#2563eb",
322
+ buttonTextColor: "#ffffff",
382
323
  },
383
324
  }
325
+ ```
384
326
 
385
- const customerPortalTheme = {
386
- tabs: {
387
- background: "#111827",
388
- borderColor: "#243044",
389
- },
327
+ When the Pricing Table is embedded inside the Customer Portal, it automatically uses `theme.customerPortal.plans` for plan styling so both widgets stay consistent.
390
328
 
391
- section: {
392
- background: "#020617",
393
- },
329
+ ---
394
330
 
395
- card: {
396
- titleColor: "#F9FAFB",
397
- details: {
398
- labelColor: "#CBD5E1",
399
- valueColor: "#FFFFFF",
400
- },
401
- },
402
- }
331
+ ### Full example
403
332
 
404
- const theme = {
405
- customerPortal: customerPortalTheme,
406
- pricingTableTheme: pricingTableTheme,
407
- }
333
+ ```tsx
334
+ import { metrifoxInit } from "@metrifox/react-sdk"
408
335
 
409
336
  metrifoxInit({
410
337
  clientKey: "your-client-key",
411
- theme,
412
- pricingTableTheme, // deprecated - Moved to `theme.pricingTable`
338
+ theme: {
339
+ customerPortal: {
340
+ general: { linkColor: "#2563eb", backgroundColor: "#ffffff" },
341
+ tabs: {
342
+ tabBackground: "#ffffff",
343
+ activeTabBackground: "#2563eb",
344
+ activeTabTextColor: "#ffffff",
345
+ inactiveTabTextColor: "#6b7280",
346
+ },
347
+ sections: {
348
+ background: "#ffffff",
349
+ content: { background: "#f4f4f5", borderRadius: "8px" },
350
+ },
351
+ buttons: {
352
+ primary: { backgroundColor: "#2563eb", typography: { color: "#ffffff" } },
353
+ },
354
+ },
355
+ pricingTable: {
356
+ plans: {
357
+ planCards: { background: "#ffffff", border: { color: "#e5e7eb", radius: "8px" } },
358
+ planButton: { background: "#2563eb", textColor: "#ffffff" },
359
+ },
360
+ tabs: { activeText: "#2563eb", indicator: "#2563eb" },
361
+ checkoutBar: { buttonBackground: "#2563eb", buttonTextColor: "#ffffff" },
362
+ },
363
+ },
413
364
  })
414
365
  ```
415
366
 
367
+ Per-widget overrides (optional):
368
+
369
+ ```tsx
370
+ <CustomerPortal customerKey="..." theme={{ general: { linkColor: "#1d4ed8" } }} />
371
+ <PricingTable checkoutUsername="..." productKey="..." theme={{ plans: { planCards: { background: "#f8fafc" } } }} />
372
+ ```
373
+
416
374
  ---
417
375
 
418
376
  ## Local Development