@slyxup/ui 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +16 -0
  3. package/LICENSE +21 -0
  4. package/dist/components/BillingPortal/BillingPortal.d.ts +22 -0
  5. package/dist/components/BillingPortal/BillingPortal.d.ts.map +1 -0
  6. package/dist/components/BillingPortal/BillingPortal.js +61 -0
  7. package/dist/components/BillingPortal/BillingPortal.js.map +1 -0
  8. package/dist/components/EmailVerification/EmailVerification.d.ts.map +1 -1
  9. package/dist/components/EmailVerification/EmailVerification.js +2 -0
  10. package/dist/components/EmailVerification/EmailVerification.js.map +1 -1
  11. package/dist/components/ForgotPassword/ForgotPassword.d.ts.map +1 -1
  12. package/dist/components/ForgotPassword/ForgotPassword.js +2 -0
  13. package/dist/components/ForgotPassword/ForgotPassword.js.map +1 -1
  14. package/dist/components/PricingTable/PricingTable.d.ts +19 -0
  15. package/dist/components/PricingTable/PricingTable.d.ts.map +1 -0
  16. package/dist/components/PricingTable/PricingTable.js +54 -0
  17. package/dist/components/PricingTable/PricingTable.js.map +1 -0
  18. package/dist/components/ResetPassword/ResetPassword.d.ts.map +1 -1
  19. package/dist/components/ResetPassword/ResetPassword.js +2 -0
  20. package/dist/components/ResetPassword/ResetPassword.js.map +1 -1
  21. package/dist/components/SignIn/SignIn.d.ts.map +1 -1
  22. package/dist/components/SignIn/SignIn.js +16 -3
  23. package/dist/components/SignIn/SignIn.js.map +1 -1
  24. package/dist/components/SignUp/SignUp.d.ts.map +1 -1
  25. package/dist/components/SignUp/SignUp.js +16 -3
  26. package/dist/components/SignUp/SignUp.js.map +1 -1
  27. package/dist/components/SocialButtons/SocialButtons.d.ts +1 -1
  28. package/dist/components/SocialButtons/SocialButtons.d.ts.map +1 -1
  29. package/dist/components/SocialButtons/SocialButtons.js +7 -2
  30. package/dist/components/SocialButtons/SocialButtons.js.map +1 -1
  31. package/dist/components/UserButton/UserButton.d.ts.map +1 -1
  32. package/dist/components/UserButton/UserButton.js +2 -0
  33. package/dist/components/UserButton/UserButton.js.map +1 -1
  34. package/dist/components/UserProfile/UserProfile.d.ts.map +1 -1
  35. package/dist/components/UserProfile/UserProfile.js +2 -0
  36. package/dist/components/UserProfile/UserProfile.js.map +1 -1
  37. package/dist/styles.d.ts +3 -2
  38. package/dist/styles.d.ts.map +1 -1
  39. package/dist/styles.js +37 -16
  40. package/dist/styles.js.map +1 -1
  41. package/package.json +13 -9
  42. package/src/components/BillingPortal/BillingPortal.tsx +184 -0
  43. package/src/components/EmailVerification/EmailVerification.tsx +2 -0
  44. package/src/components/ForgotPassword/ForgotPassword.tsx +2 -0
  45. package/src/components/PricingTable/PricingTable.tsx +157 -0
  46. package/src/components/ResetPassword/ResetPassword.tsx +2 -0
  47. package/src/components/SignIn/SignIn.tsx +29 -2
  48. package/src/components/SignUp/SignUp.tsx +29 -2
  49. package/src/components/SocialButtons/SocialButtons.tsx +10 -3
  50. package/src/components/UserButton/UserButton.tsx +2 -0
  51. package/src/components/UserProfile/UserProfile.tsx +2 -0
  52. package/src/styles.ts +38 -16
  53. package/test/components.test.tsx +135 -0
  54. package/test/icons.test.tsx +32 -0
  55. package/test/styles.test.ts +47 -0
  56. package/vitest.config.ts +2 -0
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useAuth, useUser } from '@slyxup/react';
3
3
  import { useEffect, useState } from 'react';
4
+ import { injectStyles } from '../../styles';
4
5
  /** Edit first/last name + avatar URL. */
5
6
  export function UserProfile() {
7
+ injectStyles();
6
8
  const { isLoaded, user, reload } = useUser();
7
9
  const { client } = useAuth();
8
10
  const [firstName, setFirstName] = useState('');
@@ -1 +1 @@
1
- {"version":3,"file":"UserProfile.js","sourceRoot":"","sources":["../../../src/components/UserProfile/UserProfile.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"UserProfile.js","sourceRoot":"","sources":["../../../src/components/UserProfile/UserProfile.tsx"],"names":[],"mappings":"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"}
package/dist/styles.d.ts CHANGED
@@ -2,7 +2,8 @@
2
2
  * SlyxUp UI — design tokens + stylesheet.
3
3
  * Injected once via <SlyxUpStyles />. Theme with CSS variables on :root or .slyxup-scope.
4
4
  */
5
- export declare const CSS = "\n.slyxup-root {\n --slx-accent: #5b5bd6;\n --slx-accent-hover: #4c4cc4;\n --slx-accent-soft: rgba(91, 91, 214, 0.1);\n --slx-bg: #ffffff;\n --slx-bg-page: #f7f7fa;\n --slx-ink: #16161d;\n --slx-muted: #6f6f7b;\n --slx-border: #e6e6ec;\n --slx-danger: #d64550;\n --slx-radius: 12px;\n --slx-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n --slx-mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n\n font-family: var(--slx-font);\n color: var(--slx-ink);\n -webkit-font-smoothing: antialiased;\n}\n@media (prefers-color-scheme: dark) {\n .slyxup-root:not(.slyxup-light) {\n --slx-accent: #8484f2;\n --slx-accent-hover: #9696f5;\n --slx-accent-soft: rgba(132, 132, 242, 0.14);\n --slx-bg: #191920;\n --slx-bg-page: #121218;\n --slx-ink: #f2f2f5;\n --slx-muted: #9a9aa6;\n --slx-border: #2c2c36;\n --slx-danger: #f0737d;\n }\n}\n\n@keyframes slx-shake {\n 10%, 90% { transform: translateX(-1px); }\n 20%, 80% { transform: translateX(2px); }\n 30%, 50%, 70% { transform: translateX(-4px); }\n 40%, 60% { transform: translateX(4px); }\n}\n@keyframes slx-spin { to { transform: rotate(360deg); } }\n@media (prefers-reduced-motion: reduce) {\n .slyxup-root * { animation: none !important; transition: none !important; }\n}\n\n/* \u2500\u2500 Card \u2500\u2500 */\n.slx-card {\n width: 100%;\n max-width: 380px;\n background: var(--slx-bg);\n border: 1px solid var(--slx-border);\n border-radius: calc(var(--slx-radius) + 4px);\n padding: 32px;\n box-shadow: 0 1px 2px rgba(10,10,20,.04), 0 8px 24px rgba(10,10,20,.06);\n box-sizing: border-box;\n}\n.slx-card-error { animation: slx-shake .45s cubic-bezier(.36,.07,.19,.97) both; }\n\n/* \u2500\u2500 Header / keyhole mark \u2500\u2500 */\n.slx-mark {\n width: 44px; height: 44px; border-radius: 12px;\n display: flex; align-items: center; justify-content: center;\n background: linear-gradient(135deg, var(--slx-accent), #9a6cf0);\n margin-bottom: 18px;\n}\n.slx-mark svg { display: block; }\n.slx-title { font-size: 19px; font-weight: 650; letter-spacing: -0.02em; margin: 0 0 6px; }\n.slx-subtitle { font-size: 13.5px; color: var(--slx-muted); margin: 0 0 22px; line-height: 1.5; }\n\n/* \u2500\u2500 Fields \u2500\u2500 */\n.slx-field { margin-bottom: 14px; }\n.slx-label { display: block; font-size: 12.5px; font-weight: 550; margin-bottom: 6px; letter-spacing: 0.01em; }\n.slx-input {\n width: 100%; box-sizing: border-box;\n font: inherit; font-size: 14px; color: var(--slx-ink);\n background: transparent;\n border: 1px solid var(--slx-border);\n border-radius: var(--slx-radius);\n padding: 10px 12px;\n outline: none;\n transition: border-color .15s, box-shadow .15s;\n}\n.slx-input::placeholder { color: var(--slx-muted); opacity: .75; }\n.slx-input:focus-visible {\n border-color: var(--slx-accent);\n box-shadow: 0 0 0 3px var(--slx-accent-soft);\n}\n.slx-hint { font-size: 12px; color: var(--slx-muted); margin-top: 5px; }\n.slx-error-text {\n font-size: 13px; color: var(--slx-danger);\n background: color-mix(in srgb, var(--slx-danger) 9%, transparent);\n border-radius: 8px; padding: 9px 11px; margin: 0 0 14px; line-height: 1.4;\n}\n\n/* \u2500\u2500 Button \u2500\u2500 */\n.slx-btn {\n width: 100%; box-sizing: border-box;\n font: inherit; font-size: 14px; font-weight: 600; letter-spacing: 0.01em;\n color: #fff; background: var(--slx-accent);\n border: none; border-radius: var(--slx-radius);\n padding: 11px 14px; cursor: pointer;\n display: inline-flex; align-items: center; justify-content: center; gap: 8px;\n transition: background .15s, transform .06s;\n}\n.slx-btn:hover { background: var(--slx-accent-hover); }\n.slx-btn:active { transform: scale(.985); }\n.slx-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft), 0 0 0 1px var(--slx-accent); }\n.slx-btn[disabled] { opacity: .6; cursor: not-allowed; }\n.slx-spinner {\n width: 15px; height: 15px; flex: none;\n border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;\n border-radius: 50%; animation: slx-spin .7s linear infinite;\n}\n\n/* \u2500\u2500 Social \u2500\u2500 */\n.slx-social { display: grid; gap: 10px; margin-bottom: 16px; }\n.slx-social-btn {\n width: 100%; box-sizing: border-box;\n font: inherit; font-size: 13.5px; font-weight: 550;\n color: var(--slx-ink); background: var(--slx-bg);\n border: 1px solid var(--slx-border); border-radius: var(--slx-radius);\n padding: 10px 14px; cursor: pointer;\n display: inline-flex; align-items: center; justify-content: center; gap: 10px;\n transition: background .15s, border-color .15s;\n}\n.slx-social-btn:hover { background: color-mix(in srgb, var(--slx-ink) 4%, var(--slx-bg)); }\n.slx-social-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); }\n\n/* \u2500\u2500 Divider & footer \u2500\u2500 */\n.slx-divider {\n display: flex; align-items: center; gap: 12px;\n color: var(--slx-muted); font-size: 12px;\n margin: 18px 0;\n}\n.slx-divider::before, .slx-divider::after {\n content: \"\"; height: 1px; flex: 1; background: var(--slx-border);\n}\n.slx-footer { font-size: 13px; color: var(--slx-muted); margin-top: 20px; text-align: center; }\n.slx-link { color: var(--slx-accent); font-weight: 550; text-decoration: none; cursor: pointer; }\n.slx-link:hover { text-decoration: underline; }\n.slx-link:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); border-radius: 4px; }\n\n/* \u2500\u2500 Success state \u2500\u2500 */\n.slx-success-icon {\n width: 46px; height: 46px; border-radius: 50%;\n display: flex; align-items: center; justify-content: center;\n background: color-mix(in srgb, #34a853 12%, transparent);\n color: #34a853; margin: 4px auto 18px;\n}\n\n/* \u2500\u2500 User button \u2500\u2500 */\n.slx-userbtn-wrap { position: relative; display: inline-block; }\n.slx-userbtn-avatar {\n width: 36px; height: 36px; border-radius: 50%;\n border: 1px solid var(--slx-border); cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n font-size: 14px; font-weight: 650; color: #fff;\n background: linear-gradient(135deg, var(--slx-accent), #9a6cf0);\n padding: 0; overflow: hidden;\n}\n.slx-userbtn-avatar img { width: 100%; height: 100%; object-fit: cover; }\n.slx-userbtn-avatar:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); }\n.slx-menu {\n position: absolute; right: 0; top: calc(100% + 8px);\n min-width: 230px;\n background: var(--slx-bg);\n border: 1px solid var(--slx-border);\n border-radius: var(--slx-radius);\n box-shadow: 0 8px 30px rgba(10,10,20,.14);\n overflow: hidden; z-index: 1000;\n}\n.slx-menu-header { padding: 14px 16px; border-bottom: 1px solid var(--slx-border); }\n.slx-menu-name { font-size: 14px; font-weight: 600; margin: 0 0 2px; }\n.slx-menu-email { font-size: 12.5px; color: var(--slx-muted); margin: 0; word-break: break-all; }\n.slx-menu-item {\n display: block; width: 100%; text-align: left; box-sizing: border-box;\n font: inherit; font-size: 13.5px; color: var(--slx-ink);\n background: none; border: none; padding: 10px 16px; cursor: pointer;\n}\n.slx-menu-item:hover { background: color-mix(in srgb, var(--slx-ink) 5%, transparent); }\n.slx-menu-item:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--slx-accent-soft); }\n.slx-menu-item-danger { color: var(--slx-danger); }\n";
6
- /** Inject the SlyxUp stylesheet once per document. */
5
+ export declare const FONT_LINK = "<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\"><link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin><link href=\"https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600;9..40,700;9..40,800&family=Space+Grotesk:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">";
6
+ export declare const CSS = "\n@import url('https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600;9..40,700;9..40,800&family=Space+Grotesk:wght@400;500;600;700&display=swap');\n\n.slyxup-root {\n --slx-accent: #5b5bd6;\n --slx-accent-hover: #4c4cc4;\n --slx-accent-soft: rgba(91, 91, 214, 0.14);\n --slx-bg: #ffffff;\n --slx-bg-page: #e9eaf6;\n --slx-ink: #16161d;\n --slx-muted: #6f6f7b;\n --slx-border: #d8d8e8;\n --slx-danger: #d64550;\n --slx-radius: 12px;\n --slx-font: \"DM Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n --slx-display: \"Space Grotesk\", \"DM Sans\", sans-serif;\n --slx-mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n\n font-family: var(--slx-font);\n color: var(--slx-ink);\n -webkit-font-smoothing: antialiased;\n}\n@media (prefers-color-scheme: dark) {\n .slyxup-root:not(.slyxup-light) {\n --slx-accent: #8484f2;\n --slx-accent-hover: #9696f5;\n --slx-accent-soft: rgba(132, 132, 242, 0.14);\n --slx-bg: #191920;\n --slx-bg-page: #121218;\n --slx-ink: #f2f2f5;\n --slx-muted: #9a9aa6;\n --slx-border: #2c2c36;\n --slx-danger: #f0737d;\n }\n}\n\n@keyframes slx-shake {\n 10%, 90% { transform: translateX(-1px); }\n 20%, 80% { transform: translateX(2px); }\n 30%, 50%, 70% { transform: translateX(-4px); }\n 40%, 60% { transform: translateX(4px); }\n}\n@keyframes slx-spin { to { transform: rotate(360deg); } }\n@media (prefers-reduced-motion: reduce) {\n .slyxup-root * { animation: none !important; transition: none !important; }\n}\n\n/* \u2500\u2500 Card \u2500\u2500 */\n.slx-card {\n width: 100%;\n max-width: 380px;\n background: var(--slx-bg);\n border: 1px solid var(--slx-border);\n border-radius: calc(var(--slx-radius) + 4px);\n padding: 32px;\n box-shadow: 0 4px 12px rgba(16,16,29,.08), 0 16px 40px rgba(16,16,29,.12), 0 0 0 1px rgba(16,16,29,.04);\n box-sizing: border-box;\n}\n.slyxup-root .slx-card { background: #ffffff; }\n.slx-card-error { animation: slx-shake .45s cubic-bezier(.36,.07,.19,.97) both; }\n\n/* \u2500\u2500 Header / keyhole mark \u2500\u2500 */\n.slx-mark {\n width: 44px; height: 44px; border-radius: 12px;\n display: flex; align-items: center; justify-content: center;\n background: linear-gradient(135deg, var(--slx-accent), #9a6cf0);\n margin-bottom: 18px;\n}\n.slx-mark svg { display: block; }\n.slx-title { font-family: var(--slx-display); font-size: 20px; font-weight: 650; letter-spacing: -0.02em; margin: 0 0 6px; }\n.slx-subtitle { font-size: 13.5px; color: var(--slx-muted); margin: 0 0 22px; line-height: 1.5; }\n\n/* \u2500\u2500 Fields \u2500\u2500 */\n.slx-field { margin-bottom: 14px; }\n.slx-label { display: block; font-size: 12.5px; font-weight: 550; margin-bottom: 6px; letter-spacing: 0.01em; }\n.slx-input {\n width: 100%; box-sizing: border-box;\n font: inherit; font-size: 14px; color: var(--slx-ink);\n background: transparent;\n border: 1px solid var(--slx-border);\n border-radius: var(--slx-radius);\n padding: 10px 12px;\n outline: none;\n transition: border-color .15s, box-shadow .15s;\n}\n.slx-input::placeholder { color: var(--slx-muted); opacity: .75; }\n.slx-input:focus-visible {\n border-color: var(--slx-accent);\n box-shadow: 0 0 0 3px var(--slx-accent-soft);\n}\n.slx-hint { font-size: 12px; color: var(--slx-muted); margin-top: 5px; }\n.slx-error-text {\n font-size: 13px; color: var(--slx-danger);\n background: color-mix(in srgb, var(--slx-danger) 9%, transparent);\n border-radius: 8px; padding: 9px 11px; margin: 0 0 14px; line-height: 1.4;\n}\n\n/* \u2500\u2500 Button \u2500\u2500 */\n.slx-btn {\n width: 100%; box-sizing: border-box;\n font-family: var(--slx-font); font-size: 14px; font-weight: 600; letter-spacing: 0.01em;\n color: #fff; background: #0a0a0f;\n border: 1px solid #0a0a0f; border-radius: var(--slx-radius);\n padding: 11px 14px; cursor: pointer;\n display: inline-flex; align-items: center; justify-content: center; gap: 8px;\n transition: background .15s, transform .06s, border-color .15s;\n}\n.slx-btn:hover { background: #1a1a23; border-color: #1a1a23; }\n.slx-btn:active { transform: scale(.985); background: #000; }\n.slx-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(10,10,15,.14), 0 0 0 1px #0a0a0f; }\n.slx-btn[disabled] { opacity: .6; cursor: not-allowed; }\n@media (prefers-color-scheme: dark) {\n .slyxup-root:not(.slyxup-light) .slx-btn { background: #f2f2f5; color: #0a0a0f; border-color: #f2f2f5; }\n .slyxup-root:not(.slyxup-light) .slx-btn:hover { background: #e6e6eb; border-color: #e6e6eb; }\n}\n.slx-spinner {\n width: 15px; height: 15px; flex: none;\n border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;\n border-radius: 50%; animation: slx-spin .7s linear infinite;\n}\n\n/* \u2500\u2500 Social \u2500\u2500 */\n.slx-social { display: grid; gap: 10px; margin-bottom: 16px; }\n.slx-social-btn {\n width: 100%; box-sizing: border-box;\n font: inherit; font-size: 13.5px; font-weight: 550;\n color: var(--slx-ink); background: var(--slx-bg);\n border: 1px solid var(--slx-border); border-radius: var(--slx-radius);\n padding: 10px 14px; cursor: pointer;\n display: inline-flex; align-items: center; justify-content: center; gap: 10px;\n transition: background .15s, border-color .15s;\n}\n.slx-social-btn:hover { background: color-mix(in srgb, var(--slx-ink) 4%, var(--slx-bg)); }\n.slx-social-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); }\n\n/* \u2500\u2500 Divider & footer \u2500\u2500 */\n.slx-divider {\n display: flex; align-items: center; gap: 12px;\n color: var(--slx-muted); font-size: 12px;\n margin: 18px 0;\n}\n.slx-divider::before, .slx-divider::after {\n content: \"\"; height: 1px; flex: 1; background: var(--slx-border);\n}\n.slx-footer { font-size: 13px; color: var(--slx-muted); margin-top: 20px; text-align: center; }\n.slx-link {\n color: var(--slx-accent); font-weight: 600; text-decoration: none; cursor: pointer;\n background: none; border: none; font: inherit; font-size: inherit;\n padding: 0; margin: 0;\n}\n.slx-link:hover { text-decoration: underline; color: var(--slx-accent-hover); }\n.slx-link:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); border-radius: 4px; }\n\n/* \u2500\u2500 Success state \u2500\u2500 */\n.slx-success-icon {\n width: 46px; height: 46px; border-radius: 50%;\n display: flex; align-items: center; justify-content: center;\n background: color-mix(in srgb, #34a853 12%, transparent);\n color: #34a853; margin: 4px auto 18px;\n}\n\n/* \u2500\u2500 User button \u2500\u2500 */\n.slx-userbtn-wrap { position: relative; display: inline-block; }\n.slx-userbtn-avatar {\n width: 36px; height: 36px; border-radius: 50%;\n border: 1px solid var(--slx-border); cursor: pointer;\n display: flex; align-items: center; justify-content: center;\n font-size: 14px; font-weight: 650; color: #fff;\n background: linear-gradient(135deg, var(--slx-accent), #9a6cf0);\n padding: 0; overflow: hidden;\n}\n.slx-userbtn-avatar img { width: 100%; height: 100%; object-fit: cover; }\n.slx-userbtn-avatar:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); }\n.slx-menu {\n position: absolute; right: 0; top: calc(100% + 8px);\n min-width: 230px;\n background: var(--slx-bg);\n border: 1px solid var(--slx-border);\n border-radius: var(--slx-radius);\n box-shadow: 0 8px 30px rgba(10,10,20,.14);\n overflow: hidden; z-index: 1000;\n}\n.slx-menu-header { padding: 14px 16px; border-bottom: 1px solid var(--slx-border); }\n.slx-menu-name { font-size: 14px; font-weight: 600; margin: 0 0 2px; }\n.slx-menu-email { font-size: 12.5px; color: var(--slx-muted); margin: 0; word-break: break-all; }\n.slx-menu-item {\n display: block; width: 100%; text-align: left; box-sizing: border-box;\n font: inherit; font-size: 13.5px; color: var(--slx-ink);\n background: none; border: none; padding: 10px 16px; cursor: pointer;\n}\n.slx-menu-item:hover { background: color-mix(in srgb, var(--slx-ink) 5%, transparent); }\n.slx-menu-item:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--slx-accent-soft); }\n.slx-menu-item-danger { color: var(--slx-danger); }\n";
7
+ /** Inject the SlyxUp stylesheet + DM Sans font once per document. */
7
8
  export declare function injectStyles(): void;
8
9
  //# sourceMappingURL=styles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,GAAG,mxOAqLf,CAAC;AAIF,sDAAsD;AACtD,wBAAgB,YAAY,IAAI,IAAI,CAOnC"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,SAAS,4VAAgV,CAAC;AAEvW,eAAO,MAAM,GAAG,4iQAiMf,CAAC;AAIF,qEAAqE;AACrE,wBAAgB,YAAY,IAAI,IAAI,CAenC"}
package/dist/styles.js CHANGED
@@ -2,19 +2,23 @@
2
2
  * SlyxUp UI — design tokens + stylesheet.
3
3
  * Injected once via <SlyxUpStyles />. Theme with CSS variables on :root or .slyxup-scope.
4
4
  */
5
+ export const FONT_LINK = `<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600;9..40,700;9..40,800&family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">`;
5
6
  export const CSS = `
7
+ @import url('https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600;9..40,700;9..40,800&family=Space+Grotesk:wght@400;500;600;700&display=swap');
8
+
6
9
  .slyxup-root {
7
10
  --slx-accent: #5b5bd6;
8
11
  --slx-accent-hover: #4c4cc4;
9
- --slx-accent-soft: rgba(91, 91, 214, 0.1);
12
+ --slx-accent-soft: rgba(91, 91, 214, 0.14);
10
13
  --slx-bg: #ffffff;
11
- --slx-bg-page: #f7f7fa;
14
+ --slx-bg-page: #e9eaf6;
12
15
  --slx-ink: #16161d;
13
16
  --slx-muted: #6f6f7b;
14
- --slx-border: #e6e6ec;
17
+ --slx-border: #d8d8e8;
15
18
  --slx-danger: #d64550;
16
19
  --slx-radius: 12px;
17
- --slx-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
20
+ --slx-font: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
21
+ --slx-display: "Space Grotesk", "DM Sans", sans-serif;
18
22
  --slx-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
19
23
 
20
24
  font-family: var(--slx-font);
@@ -54,9 +58,10 @@ export const CSS = `
54
58
  border: 1px solid var(--slx-border);
55
59
  border-radius: calc(var(--slx-radius) + 4px);
56
60
  padding: 32px;
57
- box-shadow: 0 1px 2px rgba(10,10,20,.04), 0 8px 24px rgba(10,10,20,.06);
61
+ box-shadow: 0 4px 12px rgba(16,16,29,.08), 0 16px 40px rgba(16,16,29,.12), 0 0 0 1px rgba(16,16,29,.04);
58
62
  box-sizing: border-box;
59
63
  }
64
+ .slyxup-root .slx-card { background: #ffffff; }
60
65
  .slx-card-error { animation: slx-shake .45s cubic-bezier(.36,.07,.19,.97) both; }
61
66
 
62
67
  /* ── Header / keyhole mark ── */
@@ -67,7 +72,7 @@ export const CSS = `
67
72
  margin-bottom: 18px;
68
73
  }
69
74
  .slx-mark svg { display: block; }
70
- .slx-title { font-size: 19px; font-weight: 650; letter-spacing: -0.02em; margin: 0 0 6px; }
75
+ .slx-title { font-family: var(--slx-display); font-size: 20px; font-weight: 650; letter-spacing: -0.02em; margin: 0 0 6px; }
71
76
  .slx-subtitle { font-size: 13.5px; color: var(--slx-muted); margin: 0 0 22px; line-height: 1.5; }
72
77
 
73
78
  /* ── Fields ── */
@@ -98,17 +103,21 @@ export const CSS = `
98
103
  /* ── Button ── */
99
104
  .slx-btn {
100
105
  width: 100%; box-sizing: border-box;
101
- font: inherit; font-size: 14px; font-weight: 600; letter-spacing: 0.01em;
102
- color: #fff; background: var(--slx-accent);
103
- border: none; border-radius: var(--slx-radius);
106
+ font-family: var(--slx-font); font-size: 14px; font-weight: 600; letter-spacing: 0.01em;
107
+ color: #fff; background: #0a0a0f;
108
+ border: 1px solid #0a0a0f; border-radius: var(--slx-radius);
104
109
  padding: 11px 14px; cursor: pointer;
105
110
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
106
- transition: background .15s, transform .06s;
111
+ transition: background .15s, transform .06s, border-color .15s;
107
112
  }
108
- .slx-btn:hover { background: var(--slx-accent-hover); }
109
- .slx-btn:active { transform: scale(.985); }
110
- .slx-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft), 0 0 0 1px var(--slx-accent); }
113
+ .slx-btn:hover { background: #1a1a23; border-color: #1a1a23; }
114
+ .slx-btn:active { transform: scale(.985); background: #000; }
115
+ .slx-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(10,10,15,.14), 0 0 0 1px #0a0a0f; }
111
116
  .slx-btn[disabled] { opacity: .6; cursor: not-allowed; }
117
+ @media (prefers-color-scheme: dark) {
118
+ .slyxup-root:not(.slyxup-light) .slx-btn { background: #f2f2f5; color: #0a0a0f; border-color: #f2f2f5; }
119
+ .slyxup-root:not(.slyxup-light) .slx-btn:hover { background: #e6e6eb; border-color: #e6e6eb; }
120
+ }
112
121
  .slx-spinner {
113
122
  width: 15px; height: 15px; flex: none;
114
123
  border: 2px solid rgba(255,255,255,.35); border-top-color: #fff;
@@ -139,8 +148,12 @@ export const CSS = `
139
148
  content: ""; height: 1px; flex: 1; background: var(--slx-border);
140
149
  }
141
150
  .slx-footer { font-size: 13px; color: var(--slx-muted); margin-top: 20px; text-align: center; }
142
- .slx-link { color: var(--slx-accent); font-weight: 550; text-decoration: none; cursor: pointer; }
143
- .slx-link:hover { text-decoration: underline; }
151
+ .slx-link {
152
+ color: var(--slx-accent); font-weight: 600; text-decoration: none; cursor: pointer;
153
+ background: none; border: none; font: inherit; font-size: inherit;
154
+ padding: 0; margin: 0;
155
+ }
156
+ .slx-link:hover { text-decoration: underline; color: var(--slx-accent-hover); }
144
157
  .slx-link:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--slx-accent-soft); border-radius: 4px; }
145
158
 
146
159
  /* ── Success state ── */
@@ -185,10 +198,18 @@ export const CSS = `
185
198
  .slx-menu-item-danger { color: var(--slx-danger); }
186
199
  `;
187
200
  let injected = false;
188
- /** Inject the SlyxUp stylesheet once per document. */
201
+ /** Inject the SlyxUp stylesheet + DM Sans font once per document. */
189
202
  export function injectStyles() {
190
203
  if (injected || typeof document === 'undefined')
191
204
  return;
205
+ // DM Sans font link
206
+ if (!document.querySelector('link[href*="DM+Sans"]')) {
207
+ const fontLink = document.createElement('link');
208
+ fontLink.rel = 'stylesheet';
209
+ fontLink.href =
210
+ 'https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600;9..40,700;9..40,800&family=Space+Grotesk:wght@400;500;600;700&display=swap';
211
+ document.head.appendChild(fontLink);
212
+ }
192
213
  const style = document.createElement('style');
193
214
  style.setAttribute('data-slyxup', 'styles');
194
215
  style.textContent = CSS;
@@ -1 +1 @@
1
- {"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqLlB,CAAC;AAEF,IAAI,QAAQ,GAAG,KAAK,CAAC;AAErB,sDAAsD;AACtD,MAAM,UAAU,YAAY;IAC1B,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC5C,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACjC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,6UAA6U,CAAC;AAEvW,MAAM,CAAC,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiMlB,CAAC;AAEF,IAAI,QAAQ,GAAG,KAAK,CAAC;AAErB,qEAAqE;AACrE,MAAM,UAAU,YAAY;IAC1B,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IACxD,oBAAoB;IACpB,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,uBAAuB,CAAC,EAAE,CAAC;QACrD,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAChD,QAAQ,CAAC,GAAG,GAAG,YAAY,CAAC;QAC5B,QAAQ,CAAC,IAAI;YACX,+KAA+K,CAAC;QAClL,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC5C,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACjC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,14 +1,10 @@
1
1
  {
2
2
  "name": "@slyxup/ui",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "@slyxup/ui — SlyxUp SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
- "scripts": {
9
- "build": "tsc -p tsconfig.json",
10
- "typecheck": "tsc --noEmit"
11
- },
12
8
  "repository": {
13
9
  "type": "git",
14
10
  "url": "git+https://github.com/slyxup/stack.git",
@@ -27,11 +23,19 @@
27
23
  "react": "^18 || ^19"
28
24
  },
29
25
  "dependencies": {
30
- "@slyxup/core": "workspace:*",
31
- "@slyxup/react": "workspace:*"
26
+ "@slyxup/core": "0.2.1",
27
+ "@slyxup/react": "0.2.1"
32
28
  },
33
29
  "devDependencies": {
34
30
  "@types/node": "^26.2.0",
35
- "@types/react": "^18"
31
+ "@types/react": "^18",
32
+ "react": "^18.3.1",
33
+ "react-dom": "^18.3.1",
34
+ "vitest": "^4.1.11"
35
+ },
36
+ "scripts": {
37
+ "build": "tsc -p tsconfig.json",
38
+ "typecheck": "tsc --noEmit",
39
+ "test": "vitest run --passWithNoTests"
36
40
  }
37
- }
41
+ }
@@ -0,0 +1,184 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import { injectStyles } from '../../styles';
5
+
6
+ interface Subscription {
7
+ id: string;
8
+ status: string;
9
+ currentPeriodEnd: string | null;
10
+ cancelAtPeriodEnd: boolean;
11
+ }
12
+ interface Invoice {
13
+ id: string;
14
+ amount: number;
15
+ currency: string;
16
+ status: 'paid' | 'pending' | 'overdue' | 'refunded';
17
+ billedAt: string | null;
18
+ }
19
+
20
+ export interface BillingPortalProps {
21
+ subscription: Subscription | null;
22
+ invoices: Invoice[];
23
+ onCancel?: () => void;
24
+ }
25
+
26
+ /** Current plan + invoices table */
27
+ export function BillingPortal({
28
+ subscription,
29
+ invoices,
30
+ onCancel,
31
+ }: BillingPortalProps) {
32
+ injectStyles();
33
+ if (!subscription) {
34
+ return (
35
+ <div className="slx-card">
36
+ <h3 style={{ fontSize: 16, fontWeight: 600 }}>
37
+ No active subscription
38
+ </h3>
39
+ <p style={{ fontSize: 13.5, color: 'var(--slx-muted)', marginTop: 6 }}>
40
+ You don&apos;t have a subscription yet. Choose a plan to get started.
41
+ </p>
42
+ </div>
43
+ );
44
+ }
45
+
46
+ return (
47
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
48
+ {/* Plan card */}
49
+ <div className="slx-card">
50
+ <div
51
+ style={{
52
+ display: 'flex',
53
+ justifyContent: 'space-between',
54
+ alignItems: 'center',
55
+ }}
56
+ >
57
+ <div>
58
+ <h3 style={{ fontSize: 15, fontWeight: 600 }}>Current plan</h3>
59
+ <p
60
+ style={{ fontSize: 13, color: 'var(--slx-muted)', marginTop: 4 }}
61
+ >
62
+ Status:{' '}
63
+ <span
64
+ style={{
65
+ fontWeight: 650,
66
+ color:
67
+ subscription.status === 'active' ||
68
+ subscription.status === 'trialing'
69
+ ? '#34d399'
70
+ : subscription.status === 'past_due'
71
+ ? 'var(--slx-danger)'
72
+ : 'var(--slx-muted)',
73
+ }}
74
+ >
75
+ {subscription.status}
76
+ </span>
77
+ </p>
78
+ {subscription.currentPeriodEnd && (
79
+ <p
80
+ style={{
81
+ fontSize: 12.5,
82
+ color: 'var(--slx-muted)',
83
+ marginTop: 4,
84
+ }}
85
+ >
86
+ Renews{' '}
87
+ {new Date(subscription.currentPeriodEnd).toLocaleDateString()}
88
+ {subscription.cancelAtPeriodEnd
89
+ ? ' (cancels at period end)'
90
+ : ''}
91
+ </p>
92
+ )}
93
+ </div>
94
+ {subscription.status !== 'canceled' &&
95
+ !subscription.cancelAtPeriodEnd &&
96
+ onCancel && (
97
+ <button
98
+ type="button"
99
+ onClick={onCancel}
100
+ style={{
101
+ fontSize: 13,
102
+ fontWeight: 550,
103
+ background: 'transparent',
104
+ border: '1px solid var(--slx-border)',
105
+ borderRadius: 'var(--slx-radius)',
106
+ padding: '8px 14px',
107
+ cursor: 'pointer',
108
+ color: 'var(--slx-danger)',
109
+ }}
110
+ >
111
+ Cancel
112
+ </button>
113
+ )}
114
+ </div>
115
+ </div>
116
+
117
+ {/* Invoices */}
118
+ {invoices.length > 0 && (
119
+ <div className="slx-card">
120
+ <h3 style={{ fontSize: 15, fontWeight: 600, marginBottom: 12 }}>
121
+ Invoices
122
+ </h3>
123
+ <table
124
+ style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}
125
+ >
126
+ <thead>
127
+ <tr
128
+ style={{
129
+ borderBottom: '1px solid var(--slx-border)',
130
+ textAlign: 'left',
131
+ }}
132
+ >
133
+ <th style={{ padding: '6px 0', fontWeight: 550 }}>Date</th>
134
+ <th style={{ padding: '6px 0', fontWeight: 550 }}>Amount</th>
135
+ <th style={{ padding: '6px 0', fontWeight: 550 }}>Status</th>
136
+ </tr>
137
+ </thead>
138
+ <tbody>
139
+ {invoices.map((inv) => (
140
+ <tr
141
+ key={inv.id}
142
+ style={{ borderBottom: '1px solid var(--slx-border)' }}
143
+ >
144
+ <td style={{ padding: '8px 0' }}>
145
+ {inv.billedAt
146
+ ? new Date(inv.billedAt).toLocaleDateString()
147
+ : '—'}
148
+ </td>
149
+ <td style={{ padding: '8px 0' }}>
150
+ ${(inv.amount / 100).toFixed(2)} {inv.currency}
151
+ </td>
152
+ <td style={{ padding: '8px 0' }}>
153
+ <span
154
+ style={{
155
+ fontSize: 11.5,
156
+ fontWeight: 600,
157
+ padding: '2px 8px',
158
+ borderRadius: 999,
159
+ background:
160
+ inv.status === 'paid'
161
+ ? 'rgba(52,211,153,.1)'
162
+ : inv.status === 'overdue'
163
+ ? 'rgba(214,69,80,.1)'
164
+ : 'rgba(108,108,232,.08)',
165
+ color:
166
+ inv.status === 'paid'
167
+ ? '#34d399'
168
+ : inv.status === 'overdue'
169
+ ? 'var(--slx-danger)'
170
+ : 'var(--slx-accent)',
171
+ }}
172
+ >
173
+ {inv.status}
174
+ </span>
175
+ </td>
176
+ </tr>
177
+ ))}
178
+ </tbody>
179
+ </table>
180
+ </div>
181
+ )}
182
+ </div>
183
+ );
184
+ }
@@ -1,5 +1,6 @@
1
1
  import { type FormEvent, useEffect, useState } from 'react';
2
2
  import { CheckIcon, KeyholeMark } from '../../icons';
3
+ import { injectStyles } from '../../styles';
3
4
 
4
5
  export interface EmailVerificationProps {
5
6
  /** Verification token (from email link ?token=...). If absent, shows resend form. */
@@ -14,6 +15,7 @@ export function EmailVerification({
14
15
  apiUrl,
15
16
  onSuccess,
16
17
  }: EmailVerificationProps) {
18
+ injectStyles();
17
19
  const [status, setStatus] = useState<
18
20
  'verifying' | 'success' | 'error' | 'resend'
19
21
  >(token ? 'verifying' : 'resend');
@@ -1,6 +1,7 @@
1
1
  import { SlyxupClient } from '@slyxup/core';
2
2
  import { type FormEvent, useEffect, useState } from 'react';
3
3
  import { CheckIcon, KeyholeMark } from '../../icons';
4
+ import { injectStyles } from '../../styles';
4
5
 
5
6
  export interface ForgotPasswordProps {
6
7
  apiUrl?: string;
@@ -14,6 +15,7 @@ export function ForgotPassword({
14
15
  onSuccess,
15
16
  onBackToSignIn,
16
17
  }: ForgotPasswordProps) {
18
+ injectStyles();
17
19
  const [email, setEmail] = useState('');
18
20
  const [sent, setSent] = useState(false);
19
21
  const [busy, setBusy] = useState(false);
@@ -0,0 +1,157 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import { injectStyles } from '../../styles';
5
+
6
+ interface Plan {
7
+ id: string;
8
+ name: string;
9
+ amount: number;
10
+ currency: string;
11
+ interval: 'month' | 'year';
12
+ trialDays: number | null;
13
+ features: string[] | null;
14
+ isPopular: boolean;
15
+ }
16
+
17
+ export interface PricingTableProps {
18
+ plans: Plan[];
19
+ onSelect?: (plan: Plan) => void;
20
+ loading?: boolean;
21
+ }
22
+
23
+ /** Clean pricing grid with popular badge */
24
+ export function PricingTable({ plans, onSelect, loading }: PricingTableProps) {
25
+ injectStyles();
26
+ if (loading) {
27
+ return (
28
+ <div
29
+ className="slx-pricing"
30
+ style={{
31
+ display: 'grid',
32
+ gridTemplateColumns: `repeat(${Math.min(plans.length, 3)}, 1fr)`,
33
+ gap: 16,
34
+ }}
35
+ >
36
+ {[1, 2, 3].map((i) => (
37
+ <div
38
+ key={i}
39
+ className="slx-card"
40
+ style={{ minHeight: 320 }}
41
+ aria-busy="true"
42
+ />
43
+ ))}
44
+ </div>
45
+ );
46
+ }
47
+
48
+ return (
49
+ <div
50
+ style={{
51
+ display: 'grid',
52
+ gridTemplateColumns: `repeat(${Math.min(plans.length, 3)}, 1fr)`,
53
+ gap: 16,
54
+ }}
55
+ >
56
+ {plans.map((plan) => (
57
+ <div
58
+ key={plan.id}
59
+ className={`slx-card${plan.isPopular ? ' slx-card-popular' : ''}`}
60
+ style={{
61
+ position: plan.isPopular ? 'relative' : undefined,
62
+ borderColor: plan.isPopular ? 'var(--slx-accent)' : undefined,
63
+ display: 'flex',
64
+ flexDirection: 'column',
65
+ }}
66
+ >
67
+ {plan.isPopular && (
68
+ <span
69
+ style={{
70
+ position: 'absolute',
71
+ top: -11,
72
+ right: 20,
73
+ fontSize: 10.5,
74
+ fontFamily: 'var(--slx-mono)',
75
+ background:
76
+ 'linear-gradient(135deg, var(--slx-accent), #8b5cf6)',
77
+ color: '#fff',
78
+ padding: '3px 10px',
79
+ borderRadius: 999,
80
+ }}
81
+ >
82
+ POPULAR
83
+ </span>
84
+ )}
85
+ <h3
86
+ style={{ fontSize: 15, fontWeight: 550, color: 'var(--slx-muted)' }}
87
+ >
88
+ {plan.name}
89
+ </h3>
90
+ <div style={{ margin: '8px 0 2px' }}>
91
+ <span
92
+ style={{
93
+ fontSize: 38,
94
+ fontWeight: 750,
95
+ letterSpacing: '-0.03em',
96
+ fontFamily: '"Space Grotesk",sans-serif',
97
+ }}
98
+ >
99
+ ${(plan.amount / 100).toFixed(0)}
100
+ </span>
101
+ <span style={{ fontSize: 14, color: 'var(--slx-muted)' }}>
102
+ /{plan.interval}
103
+ </span>
104
+ </div>
105
+ {plan.trialDays ? (
106
+ <p
107
+ style={{
108
+ fontSize: 12,
109
+ color: 'var(--slx-accent)',
110
+ marginBottom: 4,
111
+ }}
112
+ >
113
+ {plan.trialDays} day free trial
114
+ </p>
115
+ ) : (
116
+ <p style={{ fontSize: 12, color: 'transparent', marginBottom: 4 }}>
117
+ &nbsp;
118
+ </p>
119
+ )}
120
+ <ul style={{ listStyle: 'none', margin: '14px 0 22px', flex: 1 }}>
121
+ {((plan.features ?? []) as string[]).map((f: string) => (
122
+ <li
123
+ key={f}
124
+ style={{
125
+ fontSize: 13.5,
126
+ color: 'var(--slx-ink)',
127
+ padding: '5px 0 5px 24px',
128
+ position: 'relative',
129
+ }}
130
+ >
131
+ <span
132
+ style={{
133
+ position: 'absolute',
134
+ left: 0,
135
+ color: '#34d399',
136
+ fontWeight: 700,
137
+ }}
138
+ >
139
+ &#10003;
140
+ </span>{' '}
141
+ {f}
142
+ </li>
143
+ ))}
144
+ </ul>
145
+ <button
146
+ type="button"
147
+ className="slx-btn"
148
+ onClick={() => onSelect?.(plan)}
149
+ disabled={loading}
150
+ >
151
+ Get started
152
+ </button>
153
+ </div>
154
+ ))}
155
+ </div>
156
+ );
157
+ }
@@ -1,5 +1,6 @@
1
1
  import { type FormEvent, useEffect, useState } from 'react';
2
2
  import { CheckIcon, KeyholeMark } from '../../icons';
3
+ import { injectStyles } from '../../styles';
3
4
 
4
5
  export interface ResetPasswordProps {
5
6
  /** Reset token (from email link ?token=...) */
@@ -14,6 +15,7 @@ export function ResetPassword({
14
15
  apiUrl,
15
16
  onSuccess,
16
17
  }: ResetPasswordProps) {
18
+ injectStyles();
17
19
  const [password, setPassword] = useState('');
18
20
  const [done, setDone] = useState(false);
19
21
  const [busy, setBusy] = useState(false);