@olwiba/ui 0.2.25 → 0.2.26

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 (202) hide show
  1. package/content/docs/app/app-shell.mdx +71 -0
  2. package/content/docs/app/auth-section.mdx +73 -0
  3. package/content/docs/app/billing-panel.mdx +38 -0
  4. package/content/docs/app/empty-state.mdx +47 -0
  5. package/content/docs/app/error-page.mdx +31 -0
  6. package/content/docs/app/index.mdx +13 -0
  7. package/content/docs/app/meta.json +4 -0
  8. package/content/docs/app/onboarding-wizard.mdx +41 -0
  9. package/content/docs/app/settings-section.mdx +37 -0
  10. package/content/docs/app/team-members-panel.mdx +35 -0
  11. package/content/docs/app/update-banner.mdx +43 -0
  12. package/content/docs/components/activity-feed.mdx +42 -0
  13. package/content/docs/components/chart.mdx +47 -0
  14. package/content/docs/components/command-menu.mdx +41 -0
  15. package/content/docs/components/confirm-dialog.mdx +26 -0
  16. package/content/docs/components/context-menu.mdx +26 -0
  17. package/content/docs/components/data-table.mdx +40 -0
  18. package/content/docs/components/dock.mdx +26 -0
  19. package/content/docs/components/feature-card.mdx +26 -0
  20. package/content/docs/components/file-upload.mdx +35 -0
  21. package/content/docs/components/full-page-spinner.mdx +44 -0
  22. package/content/docs/components/glass-card.mdx +26 -0
  23. package/content/docs/components/image-card.mdx +26 -0
  24. package/content/docs/components/index.mdx +49 -0
  25. package/content/docs/components/load-more.mdx +34 -0
  26. package/content/docs/components/meta.json +35 -0
  27. package/content/docs/components/notifications-popover.mdx +34 -0
  28. package/content/docs/components/notify.mdx +40 -0
  29. package/content/docs/components/olwiba-ui-context.mdx +69 -0
  30. package/content/docs/components/page-header.mdx +42 -0
  31. package/content/docs/components/pricing-card.mdx +26 -0
  32. package/content/docs/components/register-hotkeys.mdx +50 -0
  33. package/content/docs/components/root-error-fallback.mdx +39 -0
  34. package/content/docs/components/spotlight.mdx +26 -0
  35. package/content/docs/components/stat-card.mdx +26 -0
  36. package/content/docs/components/suspensed.mdx +42 -0
  37. package/content/docs/components/testimonial-card.mdx +26 -0
  38. package/content/docs/components/theme-color-updater.mdx +40 -0
  39. package/content/docs/components/theme-switch-minimal.mdx +32 -0
  40. package/content/docs/components/version-banner.mdx +44 -0
  41. package/content/docs/email/action-email.mdx +62 -0
  42. package/content/docs/email/email-layout.mdx +58 -0
  43. package/content/docs/email/email-link-fallback.mdx +46 -0
  44. package/content/docs/email/index.mdx +32 -0
  45. package/content/docs/email/meta.json +10 -0
  46. package/content/docs/email/notice-email.mdx +51 -0
  47. package/content/docs/hooks/index.mdx +21 -0
  48. package/content/docs/hooks/meta.json +16 -0
  49. package/content/docs/hooks/use-confirm.mdx +81 -0
  50. package/content/docs/hooks/use-controlled-open.mdx +53 -0
  51. package/content/docs/hooks/use-copy-to-clipboard.mdx +26 -0
  52. package/content/docs/hooks/use-debounce.mdx +26 -0
  53. package/content/docs/hooks/use-intersection-observer.mdx +26 -0
  54. package/content/docs/hooks/use-local-storage.mdx +26 -0
  55. package/content/docs/hooks/use-media-query.mdx +26 -0
  56. package/content/docs/hooks/use-mounted.mdx +39 -0
  57. package/content/docs/hooks/use-pagination.mdx +26 -0
  58. package/content/docs/hooks/use-scrolled-past.mdx +44 -0
  59. package/content/docs/index.mdx +68 -0
  60. package/content/docs/marketing/contact-section.mdx +16 -0
  61. package/content/docs/marketing/cta-section.mdx +31 -0
  62. package/content/docs/marketing/faq-section.mdx +16 -0
  63. package/content/docs/marketing/features-section.mdx +23 -0
  64. package/content/docs/marketing/footer.mdx +16 -0
  65. package/content/docs/marketing/hero-section.mdx +16 -0
  66. package/content/docs/marketing/index.mdx +22 -0
  67. package/content/docs/marketing/meta.json +20 -0
  68. package/content/docs/marketing/navbar.mdx +16 -0
  69. package/content/docs/marketing/newsletter-section.mdx +16 -0
  70. package/content/docs/marketing/post-list.mdx +32 -0
  71. package/content/docs/marketing/pricing-section.mdx +16 -0
  72. package/content/docs/marketing/stats-section.mdx +16 -0
  73. package/content/docs/marketing/steps-section.mdx +60 -0
  74. package/content/docs/marketing/team-section.mdx +16 -0
  75. package/content/docs/marketing/testimonials-section.mdx +16 -0
  76. package/content/docs/mechanics/carousel.mdx +42 -0
  77. package/content/docs/mechanics/index.mdx +11 -0
  78. package/content/docs/mechanics/meta.json +4 -0
  79. package/content/docs/mechanics/sortable.mdx +44 -0
  80. package/content/docs/meta.json +4 -0
  81. package/content/docs/motion/animated-swap.mdx +75 -0
  82. package/content/docs/motion/count-up.mdx +31 -0
  83. package/content/docs/motion/fade-in.mdx +34 -0
  84. package/content/docs/motion/index.mdx +12 -0
  85. package/content/docs/motion/meta.json +4 -0
  86. package/content/docs/motion/page-transition.mdx +29 -0
  87. package/content/docs/motion/stagger-children.mdx +31 -0
  88. package/content/docs/overlays/index.mdx +9 -0
  89. package/content/docs/overlays/meta.json +4 -0
  90. package/content/docs/overlays/overlay.mdx +60 -0
  91. package/content/docs/overlays/underlay.mdx +56 -0
  92. package/dist/email/index.d.ts +8 -1
  93. package/dist/email/index.js +3 -3
  94. package/dist/email/index.js.map +1 -1
  95. package/docs-manifest.json +253 -0
  96. package/package.json +16 -8
  97. package/site/demos/action-email.tsx +6 -0
  98. package/site/demos/activity-feed.tsx +42 -0
  99. package/site/demos/animated-swap.tsx +42 -0
  100. package/site/demos/app-shell.tsx +47 -0
  101. package/site/demos/app-ui-page.tsx +9 -0
  102. package/site/demos/application-sidebar-block.tsx +9 -0
  103. package/site/demos/auth-section.tsx +15 -0
  104. package/site/demos/auth-split-block.tsx +9 -0
  105. package/site/demos/billing-page.tsx +157 -0
  106. package/site/demos/billing-panel.tsx +26 -0
  107. package/site/demos/carousel.tsx +34 -0
  108. package/site/demos/chart.tsx +55 -0
  109. package/site/demos/command-menu.tsx +39 -0
  110. package/site/demos/confirm-dialog.tsx +27 -0
  111. package/site/demos/contact-form-block.tsx +3 -0
  112. package/site/demos/contact-section.tsx +2 -0
  113. package/site/demos/context-menu.tsx +30 -0
  114. package/site/demos/count-up.tsx +23 -0
  115. package/site/demos/cta-section.tsx +6 -0
  116. package/site/demos/dashboard-overview-block.tsx +9 -0
  117. package/site/demos/dashboard-shell-block.tsx +10 -0
  118. package/site/demos/data-table.tsx +47 -0
  119. package/site/demos/demo-brand.tsx +23 -0
  120. package/site/demos/dock.tsx +20 -0
  121. package/site/demos/document-sidebar-block.tsx +9 -0
  122. package/site/demos/email-examples.tsx +58 -0
  123. package/site/demos/email-frame.tsx +50 -0
  124. package/site/demos/email-layout.tsx +6 -0
  125. package/site/demos/email-link-fallback.tsx +6 -0
  126. package/site/demos/email-previews.generated.ts +11 -0
  127. package/site/demos/empty-state-block.tsx +3 -0
  128. package/site/demos/empty-state.tsx +23 -0
  129. package/site/demos/error-page.tsx +5 -0
  130. package/site/demos/fade-in.tsx +18 -0
  131. package/site/demos/faq-section.tsx +6 -0
  132. package/site/demos/feature-card.tsx +13 -0
  133. package/site/demos/features-section.tsx +6 -0
  134. package/site/demos/file-upload.tsx +16 -0
  135. package/site/demos/footer.tsx +42 -0
  136. package/site/demos/full-page-spinner.tsx +9 -0
  137. package/site/demos/glass-card.tsx +14 -0
  138. package/site/demos/glow-effect.tsx +3 -0
  139. package/site/demos/gradient-background.tsx +3 -0
  140. package/site/demos/grid-pattern.tsx +3 -0
  141. package/site/demos/hero-section.tsx +6 -0
  142. package/site/demos/image-card.tsx +17 -0
  143. package/site/demos/login-block.tsx +9 -0
  144. package/site/demos/marketing-cta-block.tsx +3 -0
  145. package/site/demos/marketing-demo-data.tsx +114 -0
  146. package/site/demos/marketing-faq-block.tsx +3 -0
  147. package/site/demos/marketing-features-block.tsx +3 -0
  148. package/site/demos/marketing-footer-block.tsx +3 -0
  149. package/site/demos/marketing-hero-block.tsx +10 -0
  150. package/site/demos/marketing-nav-block.tsx +3 -0
  151. package/site/demos/marketing-newsletter-block.tsx +3 -0
  152. package/site/demos/marketing-page.tsx +52 -0
  153. package/site/demos/marketing-pricing-block.tsx +3 -0
  154. package/site/demos/marketing-stats-block.tsx +3 -0
  155. package/site/demos/marketing-team-block.tsx +3 -0
  156. package/site/demos/marketing-testimonials-block.tsx +3 -0
  157. package/site/demos/navbar.tsx +32 -0
  158. package/site/demos/newsletter-section.tsx +2 -0
  159. package/site/demos/noise-overlay.tsx +3 -0
  160. package/site/demos/not-found-block.tsx +3 -0
  161. package/site/demos/notice-email.tsx +6 -0
  162. package/site/demos/notifications-popover.tsx +27 -0
  163. package/site/demos/notify.tsx +44 -0
  164. package/site/demos/olwiba-ui-context.tsx +51 -0
  165. package/site/demos/onboarding-block.tsx +3 -0
  166. package/site/demos/onboarding-wizard.tsx +43 -0
  167. package/site/demos/overlay.tsx +72 -0
  168. package/site/demos/page-header.tsx +15 -0
  169. package/site/demos/page-transition.tsx +34 -0
  170. package/site/demos/post-list.tsx +24 -0
  171. package/site/demos/pricing-card.tsx +27 -0
  172. package/site/demos/pricing-section.tsx +6 -0
  173. package/site/demos/register-hotkeys.tsx +39 -0
  174. package/site/demos/root-error-fallback.tsx +28 -0
  175. package/site/demos/settings-layout-block.tsx +3 -0
  176. package/site/demos/settings-section.tsx +22 -0
  177. package/site/demos/sign-in-page.tsx +10 -0
  178. package/site/demos/sortable.tsx +30 -0
  179. package/site/demos/spotlight.tsx +29 -0
  180. package/site/demos/stagger-children.tsx +16 -0
  181. package/site/demos/stat-card.tsx +12 -0
  182. package/site/demos/stats-section.tsx +6 -0
  183. package/site/demos/steps-section.tsx +66 -0
  184. package/site/demos/suspensed.tsx +31 -0
  185. package/site/demos/team-members-panel.tsx +26 -0
  186. package/site/demos/team-section.tsx +2 -0
  187. package/site/demos/testimonial-card.tsx +17 -0
  188. package/site/demos/testimonials-section.tsx +6 -0
  189. package/site/demos/theme-color-updater.tsx +32 -0
  190. package/site/demos/theme-switch-minimal.tsx +10 -0
  191. package/site/demos/underlay.tsx +74 -0
  192. package/site/demos/update-banner.tsx +31 -0
  193. package/site/demos/use-copy-to-clipboard.tsx +21 -0
  194. package/site/demos/use-debounce.tsx +25 -0
  195. package/site/demos/use-intersection-observer.tsx +32 -0
  196. package/site/demos/use-local-storage.tsx +22 -0
  197. package/site/demos/use-media-query.tsx +31 -0
  198. package/site/demos/use-pagination.tsx +31 -0
  199. package/site/demos/user-profile-block.tsx +3 -0
  200. package/site/demos/version-banner.tsx +27 -0
  201. package/src/email/EmailLinkFallback.tsx +7 -1
  202. package/src/email/index.ts +3 -0
@@ -0,0 +1,42 @@
1
+ ---
2
+ title: Suspensed
3
+ description: A thin wrapper around React.Suspense with a sensible default fallback.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <ComponentPreview name="suspensed" />
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ bun add @olwiba/ui
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { Suspensed } from "@olwiba/ui";
22
+ import { Spinner } from "@olwiba/cn";
23
+ ```
24
+
25
+ ```tsx
26
+ // Default fallback is <Spinner />
27
+ <Suspensed>
28
+ <LazyComponent />
29
+ </Suspensed>
30
+
31
+ // Custom fallback
32
+ <Suspensed fallback={<Spinner className="size-4" />}>
33
+ <LazyComponent />
34
+ </Suspensed>
35
+ ```
36
+
37
+ ## API Reference
38
+
39
+ <APIReference name="Suspensed" props={[
40
+ { name: "children", type: "ReactNode", default: "-" },
41
+ { name: "fallback", type: "ReactNode", default: "<Spinner />" },
42
+ ]} />
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: TestimonialCard
3
+ description: Quote card with avatar, name, and role attribution.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <Sandbox id="testimonial-card" />
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ bun add @olwiba/ui
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { TestimonialCard } from "@olwiba/ui";
22
+ ```
23
+
24
+ ## API Reference
25
+
26
+ <APIReference name="TestimonialCard" props={[]} />
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: ThemeColorUpdater
3
+ description: Syncs a color theme string to the data-theme attribute on the document root.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <ComponentPreview name="theme-color-updater" />
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ bun add @olwiba/ui
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { ThemeColorUpdater } from "@olwiba/ui";
22
+ ```
23
+
24
+ ```tsx
25
+ <ThemeColorUpdater colorTheme="blue" />
26
+ ```
27
+
28
+ Renderless — returns `null`. Sets `data-theme` on `<html>` on every render. Your CSS targets `[data-theme]` for color tokens:
29
+
30
+ ```css
31
+ [data-theme="blue"] {
32
+ --primary: oklch(0.55 0.22 250);
33
+ }
34
+ ```
35
+
36
+ ## API Reference
37
+
38
+ <APIReference name="ThemeColorUpdater" props={[
39
+ { name: "colorTheme", type: "string", default: "-" },
40
+ ]} />
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: ThemeSwitchMinimal
3
+ description: A minimal icon button for toggling dark and light mode.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <ComponentPreview name="theme-switch-minimal" />
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ bun add @olwiba/ui
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { ThemeSwitchMinimal } from "@olwiba/ui";
22
+ ```
23
+
24
+ ```tsx
25
+ <ThemeSwitchMinimal />
26
+ ```
27
+
28
+ No props required. Reads and writes to `localStorage` under the `theme` key and toggles the `dark` class on `<html>`. Self-contained — no context or provider required.
29
+
30
+ ## API Reference
31
+
32
+ <APIReference name="ThemeSwitchMinimal" props={[]} />
@@ -0,0 +1,44 @@
1
+ ---
2
+ title: VersionBanner
3
+ description: A dismissible banner for announcing new versions or updates.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <ComponentPreview name="version-banner" />
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ bun add @olwiba/ui
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { VersionBanner } from "@olwiba/ui";
22
+ ```
23
+
24
+ ```tsx
25
+ const [show, setShow] = React.useState(true);
26
+
27
+ {show && (
28
+ <VersionBanner
29
+ version="2.1.0"
30
+ message="New components available — see what's changed."
31
+ onDismiss={() => setShow(false)}
32
+ />
33
+ )}
34
+ ```
35
+
36
+ Dismissal is stateless — the parent controls visibility. Persist across sessions with `localStorage` in your parent component.
37
+
38
+ ## API Reference
39
+
40
+ <APIReference name="VersionBanner" props={[
41
+ { name: "version", type: "string", default: "-" },
42
+ { name: "message", type: "string", default: "-" },
43
+ { name: "onDismiss", type: "() => void", default: "-" },
44
+ ]} />
@@ -0,0 +1,62 @@
1
+ ---
2
+ title: ActionEmail
3
+ description: Transactional email built around a single call to action — sign-in links, verification, password resets.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <Sandbox id="action-email" />
11
+
12
+ `ActionEmail` is the template for a message whose entire purpose is one button. It composes
13
+ [`EmailLayout`](/docs/email/email-layout) with a heading, a line of explanation, the action
14
+ itself, and — unless you turn it off — a
15
+ [`EmailLinkFallback`](/docs/email/email-link-fallback) for clients that strip buttons.
16
+
17
+ The preview above renders in a browser. A real mail client is less forgiving, so treat it as a
18
+ check on copy and structure rather than a rendering guarantee.
19
+
20
+ ## Installation
21
+
22
+ ```sh
23
+ bun add @olwiba/ui
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ```tsx
29
+ import { ActionEmail } from "@olwiba/ui/email";
30
+ import { render } from "@react-email/render";
31
+
32
+ const html = await render(
33
+ <ActionEmail
34
+ appName="Nexus Inc"
35
+ preview="Sign in to Nexus Inc"
36
+ heading="Sign in with one click"
37
+ description="This link expires in 15 minutes and can only be used once."
38
+ actionLabel="Sign in"
39
+ actionUrl={url}
40
+ />
41
+ );
42
+ ```
43
+
44
+ `preview` is the line mail clients show beside the subject in an inbox list. It is not rendered
45
+ in the message body, and leaving it vague wastes the only copy a recipient reads before
46
+ deciding whether to open.
47
+
48
+ ## API Reference
49
+
50
+ <APIReference
51
+ name="ActionEmail"
52
+ props={[
53
+ { name: "appName", type: "string", default: "-" },
54
+ { name: "preview", type: "string", default: "-" },
55
+ { name: "heading", type: "string", default: "-" },
56
+ { name: "description", type: "string", default: "-" },
57
+ { name: "actionLabel", type: "string", default: "-" },
58
+ { name: "actionUrl", type: "string", default: "-" },
59
+ { name: "brandColor", type: "string", default: "emailTheme.defaultBrandColor" },
60
+ { name: "showLinkFallback", type: "boolean", default: "true" },
61
+ ]}
62
+ />
@@ -0,0 +1,58 @@
1
+ ---
2
+ title: EmailLayout
3
+ description: The branded shell every email block is composed into — document, head, preview line, container and footer.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <Sandbox id="email-layout" />
11
+
12
+ `EmailLayout` is the shell the other blocks are built on. It owns the parts of a message that
13
+ should never vary between one email and the next: the document itself, the preview line, the
14
+ container width, the brand mark and the footer.
15
+
16
+ Use it directly when you are writing a message that neither
17
+ [`ActionEmail`](/docs/email/action-email) nor [`NoticeEmail`](/docs/email/notice-email) fits.
18
+ Both of those are this layout with a body already arranged, so reaching for one of them first
19
+ is usually the shorter path.
20
+
21
+ <Callout>
22
+ `EmailLayout` renders `<html>`, `<head>` and `<body>`. It is a whole document, so it cannot be
23
+ mounted inside a page — the preview above is a real rendered email in an iframe, not a React
24
+ tree. Render it with `@react-email/render` and send the string.
25
+ </Callout>
26
+
27
+ ## Installation
28
+
29
+ ```sh
30
+ bun add @olwiba/ui
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```tsx
36
+ import { EmailLayout } from "@olwiba/ui/email";
37
+ import { EmailText } from "@olwiba/cn/email";
38
+ import { render } from "@react-email/render";
39
+
40
+ const html = await render(
41
+ <EmailLayout appName="Nexus Inc" preview="A message from Nexus Inc">
42
+ <EmailText>Anything you put here sits inside the branded shell.</EmailText>
43
+ </EmailLayout>
44
+ );
45
+ ```
46
+
47
+ ## API Reference
48
+
49
+ <APIReference
50
+ name="EmailLayout"
51
+ props={[
52
+ { name: "appName", type: "string", default: "-" },
53
+ { name: "children", type: "ReactNode", default: "-" },
54
+ { name: "preview", type: "string", default: "-" },
55
+ { name: "brandColor", type: "string", default: "emailTheme.defaultBrandColor" },
56
+ { name: "footer", type: "ReactNode", default: "-" },
57
+ ]}
58
+ />
@@ -0,0 +1,46 @@
1
+ ---
2
+ title: EmailLinkFallback
3
+ description: The copyable URL shown beneath an email button, for clients that strip them.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <Sandbox id="email-link-fallback" />
11
+
12
+ Some mail clients remove buttons, rewrite them, or render them as bare text. When the button was
13
+ the only way to act on the message, the email becomes useless. `EmailLinkFallback` prints the
14
+ destination as plain, selectable text underneath it.
15
+
16
+ [`ActionEmail`](/docs/email/action-email) includes this already — set `showLinkFallback={false}`
17
+ to suppress it. Use it directly when you have built a message from
18
+ [`EmailLayout`](/docs/email/email-layout) and put your own button in it.
19
+
20
+ ## Installation
21
+
22
+ ```sh
23
+ bun add @olwiba/ui
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ```tsx
29
+ import { EmailLayout, EmailLinkFallback } from "@olwiba/ui/email";
30
+
31
+ <EmailLayout appName="Nexus Inc" preview="Sign in to Nexus Inc">
32
+ {/* your own heading and button */}
33
+ <EmailLinkFallback actionUrl={url} />
34
+ </EmailLayout>
35
+ ```
36
+
37
+ ## API Reference
38
+
39
+ <APIReference
40
+ name="EmailLinkFallback"
41
+ props={[
42
+ { name: "actionUrl", type: "string", default: "-" },
43
+ { name: "label", type: "string", default: '"Or copy and paste this link into your browser:"' },
44
+ { name: "brandColor", type: "string", default: "emailTheme.defaultBrandColor" },
45
+ ]}
46
+ />
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Email
3
+ description: React Email blocks for transactional messages.
4
+ ---
5
+
6
+ Email follows the same stack as web UI:
7
+
8
+ | Layer | Package | Role |
9
+ |-------|---------|------|
10
+ | Primitives | `@olwiba/cn/email` | Lego — `EmailText`, `EmailButton`, `EmailSection`, … |
11
+ | Blocks | `@olwiba/ui/email` | Real templates — `ActionEmail`, `NoticeEmail` |
12
+ | App | genesis | Copy + URLs only — imports **UI blocks** |
13
+
14
+ **Apps should import `@olwiba/ui/email`, not CN directly.**
15
+
16
+ ```tsx
17
+ import { ActionEmail } from "@olwiba/ui/email";
18
+ import { render } from "@react-email/render";
19
+
20
+ const html = await render(
21
+ <ActionEmail
22
+ appName="Genesis"
23
+ preview="Sign in"
24
+ heading="Sign in with one click"
25
+ description="This link expires soon."
26
+ actionLabel="Sign in"
27
+ actionUrl={url}
28
+ />
29
+ );
30
+ ```
31
+
32
+ Custom Olwiba email blocks belong in `@olwiba/ui/email`. New lego pieces belong in `@olwiba/cn/email`.
@@ -0,0 +1,10 @@
1
+ {
2
+ "title": "Email",
3
+ "pages": [
4
+ "index",
5
+ "action-email",
6
+ "notice-email",
7
+ "email-layout",
8
+ "email-link-fallback"
9
+ ]
10
+ }
@@ -0,0 +1,51 @@
1
+ ---
2
+ title: NoticeEmail
3
+ description: Transactional email that tells the recipient something, with no action to take.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <Sandbox id="notice-email" />
11
+
12
+ `NoticeEmail` is for messages that inform rather than ask: an export finished, a plan changed,
13
+ a job failed. It has no button, which is the point — an email with a call to action the
14
+ recipient cannot complete is worse than one that never offered it.
15
+
16
+ Reach for [`ActionEmail`](/docs/email/action-email) when there is exactly one thing to do.
17
+
18
+ ## Installation
19
+
20
+ ```sh
21
+ bun add @olwiba/ui
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```tsx
27
+ import { NoticeEmail } from "@olwiba/ui/email";
28
+ import { render } from "@react-email/render";
29
+
30
+ const html = await render(
31
+ <NoticeEmail
32
+ appName="Nexus Inc"
33
+ preview="Your export is ready"
34
+ heading="Your export is ready"
35
+ body="The workspace export you requested has finished. It stays available for seven days."
36
+ />
37
+ );
38
+ ```
39
+
40
+ ## API Reference
41
+
42
+ <APIReference
43
+ name="NoticeEmail"
44
+ props={[
45
+ { name: "appName", type: "string", default: "-" },
46
+ { name: "preview", type: "string", default: "-" },
47
+ { name: "heading", type: "string", default: "-" },
48
+ { name: "body", type: "string", default: "-" },
49
+ { name: "brandColor", type: "string", default: "emailTheme.defaultBrandColor" },
50
+ ]}
51
+ />
@@ -0,0 +1,21 @@
1
+ ---
2
+ title: Hooks
3
+ description: Utility hooks provided by @olwiba/ui.
4
+ ---
5
+
6
+ Hooks for common app patterns — all framework-agnostic and composable.
7
+
8
+ ## Available Hooks
9
+
10
+ | Hook | Description |
11
+ |------|-------------|
12
+ | [useCopyToClipboard](/docs/hooks/use-copy-to-clipboard) | Copies text to clipboard and exposes a boolean copied feedback state |
13
+ | [useConfirm](/docs/hooks/use-confirm) | Headless async confirmation dialog hook |
14
+ | [useControlledOpen](/docs/hooks/use-controlled-open) | Open/close state for modals, drawers, and dropdowns |
15
+ | [useDebounce](/docs/hooks/use-debounce) | Debounces a value — returns the stabilised value after a delay |
16
+ | [useIntersectionObserver](/docs/hooks/use-intersection-observer) | Returns a ref and a boolean for scroll-triggered visibility detection |
17
+ | [useLocalStorage](/docs/hooks/use-local-storage) | Persisted state backed by localStorage with SSR safety |
18
+ | [useMediaQuery](/docs/hooks/use-media-query) | Returns true/false for any CSS media query string |
19
+ | [useMounted](/docs/hooks/use-mounted) | Detect when the component has mounted on the client |
20
+ | [usePagination](/docs/hooks/use-pagination) | Page and offset state management — pairs with data tables and lists |
21
+ | [useScrolledPast](/docs/hooks/use-scrolled-past) | Detect when the page has scrolled past a threshold |
@@ -0,0 +1,16 @@
1
+ {
2
+ "title": "Hooks",
3
+ "pages": [
4
+ "index",
5
+ "use-confirm",
6
+ "use-controlled-open",
7
+ "use-copy-to-clipboard",
8
+ "use-debounce",
9
+ "use-intersection-observer",
10
+ "use-local-storage",
11
+ "use-media-query",
12
+ "use-mounted",
13
+ "use-pagination",
14
+ "use-scrolled-past"
15
+ ]
16
+ }
@@ -0,0 +1,81 @@
1
+ ---
2
+ title: useConfirm
3
+ description: A headless confirmation hook that returns state and handlers for wiring up any confirm dialog.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ ## Installation
11
+
12
+ ```sh
13
+ bun add @olwiba/ui
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ```tsx
19
+ import { useConfirm } from "@olwiba/ui";
20
+ ```
21
+
22
+ ```tsx
23
+ import { AlertDialog, AlertDialogContent, AlertDialogTitle,
24
+ AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from "@olwiba/cn";
25
+
26
+ function DeleteButton() {
27
+ const { confirm, isOpen, options, handleConfirm, handleCancel } = useConfirm();
28
+
29
+ const handleDelete = async () => {
30
+ const ok = await confirm({
31
+ title: 'Delete item?',
32
+ description: 'This cannot be undone.',
33
+ confirmLabel: 'Delete',
34
+ cancelLabel: 'Cancel',
35
+ });
36
+ if (ok) {
37
+ // proceed with deletion
38
+ }
39
+ };
40
+
41
+ return (
42
+ <>
43
+ <button onClick={handleDelete}>Delete</button>
44
+
45
+ <AlertDialog open={isOpen}>
46
+ <AlertDialogContent>
47
+ <AlertDialogTitle>{options.title}</AlertDialogTitle>
48
+ {options.description && <p>{options.description}</p>}
49
+ <AlertDialogFooter>
50
+ <AlertDialogCancel onClick={handleCancel}>
51
+ {options.cancelLabel ?? 'Cancel'}
52
+ </AlertDialogCancel>
53
+ <AlertDialogAction onClick={handleConfirm}>
54
+ {options.confirmLabel ?? 'Confirm'}
55
+ </AlertDialogAction>
56
+ </AlertDialogFooter>
57
+ </AlertDialogContent>
58
+ </AlertDialog>
59
+ </>
60
+ );
61
+ }
62
+ ```
63
+
64
+ `useConfirm` manages only state and async resolution — you wire up your own dialog, giving you full control over styling and which primitive to use.
65
+
66
+ ## API Reference
67
+
68
+ <APIReference name="useConfirm → returns" props={[
69
+ { name: "confirm", type: "(options?: ConfirmOptions) => Promise<boolean>", default: "-" },
70
+ { name: "isOpen", type: "boolean", default: "-" },
71
+ { name: "options", type: "ConfirmOptions", default: "-" },
72
+ { name: "handleConfirm", type: "() => void", default: "-" },
73
+ { name: "handleCancel", type: "() => void", default: "-" },
74
+ ]} />
75
+
76
+ <APIReference name="ConfirmOptions" props={[
77
+ { name: "title", type: "string", default: "-" },
78
+ { name: "description", type: "string", default: "-" },
79
+ { name: "confirmLabel", type: "string", default: "-" },
80
+ { name: "cancelLabel", type: "string", default: "-" },
81
+ ]} />
@@ -0,0 +1,53 @@
1
+ ---
2
+ title: useControlledOpen
3
+ description: Simple open/close state for modals, drawers, dropdowns, and any toggleable UI.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ ## Installation
11
+
12
+ ```sh
13
+ bun add @olwiba/ui
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ```tsx
19
+ import { useControlledOpen } from "@olwiba/ui";
20
+ ```
21
+
22
+ ```tsx
23
+ import { Dialog } from "@olwiba/cn";
24
+
25
+ function Example() {
26
+ const { isOpen, open, close, toggle, setIsOpen } = useControlledOpen();
27
+
28
+ return (
29
+ <>
30
+ <button onClick={open}>Open dialog</button>
31
+ <Dialog open={isOpen} onOpenChange={setIsOpen}>
32
+ ...
33
+ </Dialog>
34
+ </>
35
+ );
36
+ }
37
+ ```
38
+
39
+ `setIsOpen` is stable and safe to pass directly to Radix `onOpenChange` handlers. All returned functions are memoized with `useCallback`.
40
+
41
+ ## API Reference
42
+
43
+ <APIReference name="useControlledOpen" props={[
44
+ { name: "defaultOpen", type: "boolean", default: "false" },
45
+ ]} />
46
+
47
+ <APIReference name="useControlledOpen → returns" props={[
48
+ { name: "isOpen", type: "boolean", default: "-" },
49
+ { name: "open", type: "() => void", default: "-" },
50
+ { name: "close", type: "() => void", default: "-" },
51
+ { name: "toggle", type: "() => void", default: "-" },
52
+ { name: "setIsOpen", type: "Dispatch<SetStateAction<boolean>>", default: "-" },
53
+ ]} />
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: useCopyToClipboard
3
+ description: Copies text to clipboard and exposes a boolean copied feedback state.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <Sandbox id="use-copy-to-clipboard" />
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ bun add @olwiba/ui
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { useCopyToClipboard } from "@olwiba/ui";
22
+ ```
23
+
24
+ ## API Reference
25
+
26
+ <APIReference name="useCopyToClipboard" props={[]} />
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: useDebounce
3
+ description: Debounces a value — returns the stabilised value after a delay.
4
+ ---
5
+
6
+ <div className="flex flex-col md:flex-row items-center justify-start gap-2 mb-2">
7
+ <CopyCommandButton command="bun add @olwiba/ui" />
8
+ </div>
9
+
10
+ <Sandbox id="use-debounce" />
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ bun add @olwiba/ui
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```tsx
21
+ import { useDebounce } from "@olwiba/ui";
22
+ ```
23
+
24
+ ## API Reference
25
+
26
+ <APIReference name="useDebounce" props={[]} />