@ciderpress/ui 1.0.0-rc.1

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 (428) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/dist/0~142.js +2 -0
  4. package/dist/0~164.js +2 -0
  5. package/dist/0~2.js +2 -0
  6. package/dist/0~225.js +2 -0
  7. package/dist/0~288.js +2 -0
  8. package/dist/0~299.js +2 -0
  9. package/dist/0~3.js +2 -0
  10. package/dist/0~301.js +2 -0
  11. package/dist/0~311.js +2 -0
  12. package/dist/0~326.js +2 -0
  13. package/dist/0~40.js +2 -0
  14. package/dist/0~43.js +2 -0
  15. package/dist/0~433.js +2 -0
  16. package/dist/0~453.js +2 -0
  17. package/dist/0~457.js +2 -0
  18. package/dist/0~497.js +2 -0
  19. package/dist/0~511.js +2 -0
  20. package/dist/0~603.js +2 -0
  21. package/dist/0~616.js +2 -0
  22. package/dist/0~659.js +2 -0
  23. package/dist/0~670.js +2 -0
  24. package/dist/0~687.js +2 -0
  25. package/dist/0~706.js +2 -0
  26. package/dist/0~729.js +2 -0
  27. package/dist/0~768.js +2 -0
  28. package/dist/0~819.js +2 -0
  29. package/dist/0~830.js +2 -0
  30. package/dist/0~854.js +2 -0
  31. package/dist/0~863.js +2 -0
  32. package/dist/0~879.js +2 -0
  33. package/dist/0~96.js +2 -0
  34. package/dist/0~97.js +2 -0
  35. package/dist/components/FileTree/FileTree.css +90 -0
  36. package/dist/components/FileTree/FileTree.d.ts +7 -0
  37. package/dist/components/FileTree/FileTree.js +531 -0
  38. package/dist/components/FileTree/FileTreeItem.d.ts +8 -0
  39. package/dist/components/FileTree/RemoteSvgIcon.d.ts +11 -0
  40. package/dist/components/FileTree/index.d.ts +4 -0
  41. package/dist/components/folder-icons.d.ts +32 -0
  42. package/dist/components/languages.d.ts +7 -0
  43. package/dist/components/tree-parser/tree-parser.d.ts +12 -0
  44. package/dist/components/tree-parser/types.d.ts +11 -0
  45. package/dist/head/css/loader-apple.css +1 -0
  46. package/dist/head/css/loader-backdrop.css +1 -0
  47. package/dist/head/css/loader-dots.css +1 -0
  48. package/dist/head/css/themes/amber.css +1 -0
  49. package/dist/head/css/themes/arcade.css +1 -0
  50. package/dist/head/css/themes/grannysmith.css +1 -0
  51. package/dist/head/css/themes/honeycrisp.css +1 -0
  52. package/dist/head/css/themes/midnight.css +1 -0
  53. package/dist/head/css/themes/mulled.css +1 -0
  54. package/dist/head/js/loader-dots.js +1 -0
  55. package/dist/head/js/vscode-nav.js +1 -0
  56. package/dist/index.d.ts +43 -0
  57. package/dist/index.mjs +40 -0
  58. package/dist/node.d.ts +81 -0
  59. package/dist/node.mjs +17762 -0
  60. package/dist/plugins/mermaid/MermaidRenderer.tsx +480 -0
  61. package/dist/plugins/mermaid/mermaid.css +199 -0
  62. package/dist/theme/components/announcement/announcement-bar.css +103 -0
  63. package/dist/theme/components/announcement/announcement-bar.tsx +173 -0
  64. package/dist/theme/components/ask-ai/ask-ai-button.css +52 -0
  65. package/dist/theme/components/ask-ai/ask-ai-button.tsx +56 -0
  66. package/dist/theme/components/content-footer/content-footer-portal.tsx +70 -0
  67. package/dist/theme/components/content-footer/feedback.css +47 -0
  68. package/dist/theme/components/content-footer/feedback.tsx +96 -0
  69. package/dist/theme/components/content-footer/meta-actions.css +25 -0
  70. package/dist/theme/components/content-footer/meta-actions.tsx +57 -0
  71. package/dist/theme/components/content-footer/page-pager.css +61 -0
  72. package/dist/theme/components/content-footer/page-pager.tsx +110 -0
  73. package/dist/theme/components/edit-source-button.tsx +97 -0
  74. package/dist/theme/components/footer/site-footer.css +179 -0
  75. package/dist/theme/components/footer/site-footer.tsx +115 -0
  76. package/dist/theme/components/home/cta.css +67 -0
  77. package/dist/theme/components/home/cta.tsx +77 -0
  78. package/dist/theme/components/home/feature-card.css +52 -0
  79. package/dist/theme/components/home/feature-card.tsx +131 -0
  80. package/dist/theme/components/home/feature.tsx +79 -0
  81. package/dist/theme/components/home/hero-demo.css +66 -0
  82. package/dist/theme/components/home/hero-demo.tsx +46 -0
  83. package/dist/theme/components/home/hero.css +96 -0
  84. package/dist/theme/components/home/hero.tsx +111 -0
  85. package/dist/theme/components/home/layout.tsx +183 -0
  86. package/dist/theme/components/home/page-rail.css +21 -0
  87. package/dist/theme/components/home/page-rail.tsx +19 -0
  88. package/dist/theme/components/home/split.css +148 -0
  89. package/dist/theme/components/home/split.tsx +120 -0
  90. package/dist/theme/components/home/trust-strip.css +33 -0
  91. package/dist/theme/components/home/trust-strip.tsx +43 -0
  92. package/dist/theme/components/home/workspaces.tsx +71 -0
  93. package/dist/theme/components/nav/branch-tag.css +54 -0
  94. package/dist/theme/components/nav/branch-tag.tsx +101 -0
  95. package/dist/theme/components/nav/ciderpress-doc-layout.tsx +247 -0
  96. package/dist/theme/components/nav/ciderpress-docs-bar.css +505 -0
  97. package/dist/theme/components/nav/ciderpress-docs-bar.tsx +426 -0
  98. package/dist/theme/components/nav/ciderpress-header.css +172 -0
  99. package/dist/theme/components/nav/ciderpress-header.tsx +113 -0
  100. package/dist/theme/components/nav/ciderpress-layout-context.tsx +49 -0
  101. package/dist/theme/components/nav/ciderpress-nav-hamburger.css +193 -0
  102. package/dist/theme/components/nav/ciderpress-nav-hamburger.tsx +112 -0
  103. package/dist/theme/components/nav/ciderpress-nav-menu.css +130 -0
  104. package/dist/theme/components/nav/ciderpress-nav-menu.tsx +372 -0
  105. package/dist/theme/components/nav/ciderpress-nav-search.css +73 -0
  106. package/dist/theme/components/nav/ciderpress-nav-search.tsx +55 -0
  107. package/dist/theme/components/nav/ciderpress-nav-social-links.css +35 -0
  108. package/dist/theme/components/nav/ciderpress-nav-social-links.tsx +87 -0
  109. package/dist/theme/components/nav/floating-branch-indicator.css +106 -0
  110. package/dist/theme/components/nav/floating-branch-indicator.tsx +109 -0
  111. package/dist/theme/components/nav/layout.tsx +268 -0
  112. package/dist/theme/components/nav/mobile-nav-cta.tsx +79 -0
  113. package/dist/theme/components/nav/nav-divider.css +15 -0
  114. package/dist/theme/components/nav/nav-divider.tsx +19 -0
  115. package/dist/theme/components/nav/nav-logo.css +42 -0
  116. package/dist/theme/components/nav/nav-logo.tsx +262 -0
  117. package/dist/theme/components/nav/theme-switcher.css +92 -0
  118. package/dist/theme/components/nav/theme-switcher.tsx +298 -0
  119. package/dist/theme/components/nav/topbar-cta.css +45 -0
  120. package/dist/theme/components/nav/topbar-cta.tsx +39 -0
  121. package/dist/theme/components/nav/variant-toggle.css +70 -0
  122. package/dist/theme/components/nav/variant-toggle.tsx +83 -0
  123. package/dist/theme/components/nav/version-chip.css +31 -0
  124. package/dist/theme/components/nav/version-chip.tsx +45 -0
  125. package/dist/theme/components/nav/vscode-tag.css +32 -0
  126. package/dist/theme/components/nav/vscode-tag.tsx +20 -0
  127. package/dist/theme/components/openapi/code-sample.tsx +341 -0
  128. package/dist/theme/components/openapi/copy-markdown-button.tsx +41 -0
  129. package/dist/theme/components/openapi/icons.tsx +45 -0
  130. package/dist/theme/components/openapi/index.ts +6 -0
  131. package/dist/theme/components/openapi/method-badge.tsx +24 -0
  132. package/dist/theme/components/openapi/openapi.css +665 -0
  133. package/dist/theme/components/openapi/operation.tsx +200 -0
  134. package/dist/theme/components/openapi/overview.tsx +207 -0
  135. package/dist/theme/components/openapi/parameters-table.tsx +149 -0
  136. package/dist/theme/components/openapi/request-body.tsx +90 -0
  137. package/dist/theme/components/openapi/response-list.tsx +112 -0
  138. package/dist/theme/components/openapi/schema-viewer.tsx +277 -0
  139. package/dist/theme/components/openapi/security-badges.tsx +63 -0
  140. package/dist/theme/components/openapi/spec-utils.ts +122 -0
  141. package/dist/theme/components/shared/accordion.css +118 -0
  142. package/dist/theme/components/shared/accordion.stories.tsx +96 -0
  143. package/dist/theme/components/shared/accordion.tsx +191 -0
  144. package/dist/theme/components/shared/brand-logo.stories.tsx +49 -0
  145. package/dist/theme/components/shared/brand-mark.stories.tsx +53 -0
  146. package/dist/theme/components/shared/browser-window.css +1 -0
  147. package/dist/theme/components/shared/browser-window.stories.tsx +100 -0
  148. package/dist/theme/components/shared/browser-window.tsx +2 -0
  149. package/dist/theme/components/shared/card.stories.tsx +50 -0
  150. package/dist/theme/components/shared/card.tsx +35 -0
  151. package/dist/theme/components/shared/ciderpress-logo.tsx +93 -0
  152. package/dist/theme/components/shared/ciderpress-mark.tsx +77 -0
  153. package/dist/theme/components/shared/color.css +56 -0
  154. package/dist/theme/components/shared/color.stories.tsx +47 -0
  155. package/dist/theme/components/shared/color.tsx +69 -0
  156. package/dist/theme/components/shared/columns.css +37 -0
  157. package/dist/theme/components/shared/columns.stories.tsx +91 -0
  158. package/dist/theme/components/shared/columns.tsx +50 -0
  159. package/dist/theme/components/shared/desktop-window.css +339 -0
  160. package/dist/theme/components/shared/desktop-window.tsx +535 -0
  161. package/dist/theme/components/shared/field.css +158 -0
  162. package/dist/theme/components/shared/field.stories.tsx +100 -0
  163. package/dist/theme/components/shared/field.tsx +186 -0
  164. package/dist/theme/components/shared/frame.css +39 -0
  165. package/dist/theme/components/shared/frame.stories.tsx +49 -0
  166. package/dist/theme/components/shared/frame.tsx +76 -0
  167. package/dist/theme/components/shared/icon.stories.tsx +90 -0
  168. package/dist/theme/components/shared/icon.tsx +41 -0
  169. package/dist/theme/components/shared/prompt.css +178 -0
  170. package/dist/theme/components/shared/prompt.stories.tsx +57 -0
  171. package/dist/theme/components/shared/prompt.tsx +369 -0
  172. package/dist/theme/components/shared/resolve-card-icon.ts +28 -0
  173. package/dist/theme/components/shared/section-card.stories.tsx +51 -0
  174. package/dist/theme/components/shared/section-card.tsx +44 -0
  175. package/dist/theme/components/shared/section-grid.stories.tsx +46 -0
  176. package/dist/theme/components/shared/section-grid.tsx +15 -0
  177. package/dist/theme/components/shared/status-badge.css +37 -0
  178. package/dist/theme/components/shared/status-badge.stories.tsx +61 -0
  179. package/dist/theme/components/shared/status-badge.tsx +74 -0
  180. package/dist/theme/components/shared/steps.css +88 -0
  181. package/dist/theme/components/shared/steps.stories.tsx +68 -0
  182. package/dist/theme/components/shared/steps.tsx +100 -0
  183. package/dist/theme/components/shared/tech-icon-table.stories.tsx +29 -0
  184. package/dist/theme/components/shared/tech-icon-table.tsx +71 -0
  185. package/dist/theme/components/shared/tech-tag.stories.tsx +40 -0
  186. package/dist/theme/components/shared/tech-tag.tsx +31 -0
  187. package/dist/theme/components/shared/theme-color-palette.css +80 -0
  188. package/dist/theme/components/shared/theme-color-palette.stories.tsx +52 -0
  189. package/dist/theme/components/shared/theme-color-palette.tsx +275 -0
  190. package/dist/theme/components/shared/tooltip.css +69 -0
  191. package/dist/theme/components/shared/tooltip.stories.tsx +72 -0
  192. package/dist/theme/components/shared/tooltip.tsx +77 -0
  193. package/dist/theme/components/sidebar/framework-picker.css +77 -0
  194. package/dist/theme/components/sidebar/framework-picker.tsx +78 -0
  195. package/dist/theme/components/sidebar/sidebar-filter.test.ts +140 -0
  196. package/dist/theme/components/sidebar/sidebar-filter.ts +79 -0
  197. package/dist/theme/components/sidebar/sidebar-links.css +102 -0
  198. package/dist/theme/components/sidebar/sidebar-links.tsx +122 -0
  199. package/dist/theme/components/sidebar/sidebar-promo.css +42 -0
  200. package/dist/theme/components/sidebar/sidebar-promo.tsx +52 -0
  201. package/dist/theme/components/sidebar/sidebar-scope.tsx +151 -0
  202. package/dist/theme/components/sidebar/sidebar-toggle.css +181 -0
  203. package/dist/theme/components/sidebar/sidebar-toggle.tsx +127 -0
  204. package/dist/theme/components/theme-provider.tsx +637 -0
  205. package/dist/theme/components/workspaces/card.css +151 -0
  206. package/dist/theme/components/workspaces/card.tsx +166 -0
  207. package/dist/theme/components/workspaces/grid.tsx +62 -0
  208. package/dist/theme/css.d.ts +1 -0
  209. package/dist/theme/env.d.ts +12 -0
  210. package/dist/theme/fonts/GeistMono-Variable.woff2 +0 -0
  211. package/dist/theme/fonts/GeistPixel-Square.woff2 +0 -0
  212. package/dist/theme/fonts/GeistSans-Variable.woff2 +0 -0
  213. package/dist/theme/hooks/use-ciderpress.ts +61 -0
  214. package/dist/theme/hooks/use-nav-items.ts +76 -0
  215. package/dist/theme/icons/index.ts +2 -0
  216. package/dist/theme/icons/tech-map.ts +228 -0
  217. package/dist/theme/index.tsx +180 -0
  218. package/dist/theme/lib/read-social-links.ts +27 -0
  219. package/dist/theme/lib/route-link.tsx +126 -0
  220. package/dist/theme/lib/safe-url.test.ts +0 -0
  221. package/dist/theme/lib/safe-url.ts +68 -0
  222. package/dist/theme/lib/theme-favicon.ts +125 -0
  223. package/dist/theme/lib/user-config-stub.ts +12 -0
  224. package/dist/theme/styles/layers.css +32 -0
  225. package/dist/theme/styles/overrides/badge.css +26 -0
  226. package/dist/theme/styles/overrides/content-footer.css +80 -0
  227. package/dist/theme/styles/overrides/details.css +61 -0
  228. package/dist/theme/styles/overrides/fonts.css +32 -0
  229. package/dist/theme/styles/overrides/home-card.css +133 -0
  230. package/dist/theme/styles/overrides/home.css +243 -0
  231. package/dist/theme/styles/overrides/rail.css +337 -0
  232. package/dist/theme/styles/overrides/rspress.css +62 -0
  233. package/dist/theme/styles/overrides/scrollbar.css +25 -0
  234. package/dist/theme/styles/overrides/section-card.css +114 -0
  235. package/dist/theme/styles/overrides/sidebar.css +144 -0
  236. package/dist/theme/styles/overrides/tokens.css +288 -0
  237. package/dist/theme/styles/overrides/vscode.css +88 -0
  238. package/dist/theme/styles/themes/amber.css +545 -0
  239. package/dist/theme/styles/themes/arcade-fx.css +366 -0
  240. package/dist/theme/styles/themes/arcade.css +273 -0
  241. package/dist/theme/styles/themes/grannysmith.css +545 -0
  242. package/dist/theme/styles/themes/honeycrisp.css +545 -0
  243. package/dist/theme/styles/themes/midnight.css +273 -0
  244. package/dist/theme/styles/themes/mulled.css +817 -0
  245. package/package.json +103 -0
  246. package/src/theme/components/announcement/announcement-bar.css +103 -0
  247. package/src/theme/components/announcement/announcement-bar.tsx +173 -0
  248. package/src/theme/components/ask-ai/ask-ai-button.css +52 -0
  249. package/src/theme/components/ask-ai/ask-ai-button.tsx +56 -0
  250. package/src/theme/components/content-footer/content-footer-portal.tsx +70 -0
  251. package/src/theme/components/content-footer/feedback.css +47 -0
  252. package/src/theme/components/content-footer/feedback.tsx +96 -0
  253. package/src/theme/components/content-footer/meta-actions.css +25 -0
  254. package/src/theme/components/content-footer/meta-actions.tsx +57 -0
  255. package/src/theme/components/content-footer/page-pager.css +61 -0
  256. package/src/theme/components/content-footer/page-pager.tsx +110 -0
  257. package/src/theme/components/edit-source-button.tsx +97 -0
  258. package/src/theme/components/footer/site-footer.css +179 -0
  259. package/src/theme/components/footer/site-footer.tsx +115 -0
  260. package/src/theme/components/home/cta.css +67 -0
  261. package/src/theme/components/home/cta.tsx +77 -0
  262. package/src/theme/components/home/feature-card.css +52 -0
  263. package/src/theme/components/home/feature-card.tsx +131 -0
  264. package/src/theme/components/home/feature.tsx +79 -0
  265. package/src/theme/components/home/hero-demo.css +66 -0
  266. package/src/theme/components/home/hero-demo.tsx +46 -0
  267. package/src/theme/components/home/hero.css +96 -0
  268. package/src/theme/components/home/hero.tsx +111 -0
  269. package/src/theme/components/home/layout.tsx +183 -0
  270. package/src/theme/components/home/page-rail.css +21 -0
  271. package/src/theme/components/home/page-rail.tsx +19 -0
  272. package/src/theme/components/home/split.css +148 -0
  273. package/src/theme/components/home/split.tsx +120 -0
  274. package/src/theme/components/home/trust-strip.css +33 -0
  275. package/src/theme/components/home/trust-strip.tsx +43 -0
  276. package/src/theme/components/home/workspaces.tsx +71 -0
  277. package/src/theme/components/nav/branch-tag.css +54 -0
  278. package/src/theme/components/nav/branch-tag.tsx +101 -0
  279. package/src/theme/components/nav/ciderpress-doc-layout.tsx +247 -0
  280. package/src/theme/components/nav/ciderpress-docs-bar.css +505 -0
  281. package/src/theme/components/nav/ciderpress-docs-bar.tsx +426 -0
  282. package/src/theme/components/nav/ciderpress-header.css +172 -0
  283. package/src/theme/components/nav/ciderpress-header.tsx +113 -0
  284. package/src/theme/components/nav/ciderpress-layout-context.tsx +49 -0
  285. package/src/theme/components/nav/ciderpress-nav-hamburger.css +193 -0
  286. package/src/theme/components/nav/ciderpress-nav-hamburger.tsx +112 -0
  287. package/src/theme/components/nav/ciderpress-nav-menu.css +130 -0
  288. package/src/theme/components/nav/ciderpress-nav-menu.tsx +372 -0
  289. package/src/theme/components/nav/ciderpress-nav-search.css +73 -0
  290. package/src/theme/components/nav/ciderpress-nav-search.tsx +55 -0
  291. package/src/theme/components/nav/ciderpress-nav-social-links.css +35 -0
  292. package/src/theme/components/nav/ciderpress-nav-social-links.tsx +87 -0
  293. package/src/theme/components/nav/floating-branch-indicator.css +106 -0
  294. package/src/theme/components/nav/floating-branch-indicator.tsx +109 -0
  295. package/src/theme/components/nav/layout.tsx +268 -0
  296. package/src/theme/components/nav/mobile-nav-cta.tsx +79 -0
  297. package/src/theme/components/nav/nav-divider.css +15 -0
  298. package/src/theme/components/nav/nav-divider.tsx +19 -0
  299. package/src/theme/components/nav/nav-logo.css +42 -0
  300. package/src/theme/components/nav/nav-logo.tsx +262 -0
  301. package/src/theme/components/nav/theme-switcher.css +92 -0
  302. package/src/theme/components/nav/theme-switcher.tsx +298 -0
  303. package/src/theme/components/nav/topbar-cta.css +45 -0
  304. package/src/theme/components/nav/topbar-cta.tsx +39 -0
  305. package/src/theme/components/nav/variant-toggle.css +70 -0
  306. package/src/theme/components/nav/variant-toggle.tsx +83 -0
  307. package/src/theme/components/nav/version-chip.css +31 -0
  308. package/src/theme/components/nav/version-chip.tsx +45 -0
  309. package/src/theme/components/nav/vscode-tag.css +32 -0
  310. package/src/theme/components/nav/vscode-tag.tsx +20 -0
  311. package/src/theme/components/openapi/code-sample.tsx +341 -0
  312. package/src/theme/components/openapi/copy-markdown-button.tsx +41 -0
  313. package/src/theme/components/openapi/icons.tsx +45 -0
  314. package/src/theme/components/openapi/index.ts +6 -0
  315. package/src/theme/components/openapi/method-badge.tsx +24 -0
  316. package/src/theme/components/openapi/openapi.css +665 -0
  317. package/src/theme/components/openapi/operation.tsx +200 -0
  318. package/src/theme/components/openapi/overview.tsx +207 -0
  319. package/src/theme/components/openapi/parameters-table.tsx +149 -0
  320. package/src/theme/components/openapi/request-body.tsx +90 -0
  321. package/src/theme/components/openapi/response-list.tsx +112 -0
  322. package/src/theme/components/openapi/schema-viewer.tsx +277 -0
  323. package/src/theme/components/openapi/security-badges.tsx +63 -0
  324. package/src/theme/components/openapi/spec-utils.ts +122 -0
  325. package/src/theme/components/shared/accordion.css +118 -0
  326. package/src/theme/components/shared/accordion.stories.tsx +96 -0
  327. package/src/theme/components/shared/accordion.tsx +191 -0
  328. package/src/theme/components/shared/brand-logo.stories.tsx +49 -0
  329. package/src/theme/components/shared/brand-mark.stories.tsx +53 -0
  330. package/src/theme/components/shared/browser-window.css +1 -0
  331. package/src/theme/components/shared/browser-window.stories.tsx +100 -0
  332. package/src/theme/components/shared/browser-window.tsx +2 -0
  333. package/src/theme/components/shared/card.stories.tsx +50 -0
  334. package/src/theme/components/shared/card.tsx +35 -0
  335. package/src/theme/components/shared/ciderpress-logo.tsx +93 -0
  336. package/src/theme/components/shared/ciderpress-mark.tsx +77 -0
  337. package/src/theme/components/shared/color.css +56 -0
  338. package/src/theme/components/shared/color.stories.tsx +47 -0
  339. package/src/theme/components/shared/color.tsx +69 -0
  340. package/src/theme/components/shared/columns.css +37 -0
  341. package/src/theme/components/shared/columns.stories.tsx +91 -0
  342. package/src/theme/components/shared/columns.tsx +50 -0
  343. package/src/theme/components/shared/desktop-window.css +339 -0
  344. package/src/theme/components/shared/desktop-window.tsx +535 -0
  345. package/src/theme/components/shared/field.css +158 -0
  346. package/src/theme/components/shared/field.stories.tsx +100 -0
  347. package/src/theme/components/shared/field.tsx +186 -0
  348. package/src/theme/components/shared/frame.css +39 -0
  349. package/src/theme/components/shared/frame.stories.tsx +49 -0
  350. package/src/theme/components/shared/frame.tsx +76 -0
  351. package/src/theme/components/shared/icon.stories.tsx +90 -0
  352. package/src/theme/components/shared/icon.tsx +41 -0
  353. package/src/theme/components/shared/prompt.css +178 -0
  354. package/src/theme/components/shared/prompt.stories.tsx +57 -0
  355. package/src/theme/components/shared/prompt.tsx +369 -0
  356. package/src/theme/components/shared/resolve-card-icon.ts +28 -0
  357. package/src/theme/components/shared/section-card.stories.tsx +51 -0
  358. package/src/theme/components/shared/section-card.tsx +44 -0
  359. package/src/theme/components/shared/section-grid.stories.tsx +46 -0
  360. package/src/theme/components/shared/section-grid.tsx +15 -0
  361. package/src/theme/components/shared/status-badge.css +37 -0
  362. package/src/theme/components/shared/status-badge.stories.tsx +61 -0
  363. package/src/theme/components/shared/status-badge.tsx +74 -0
  364. package/src/theme/components/shared/steps.css +88 -0
  365. package/src/theme/components/shared/steps.stories.tsx +68 -0
  366. package/src/theme/components/shared/steps.tsx +100 -0
  367. package/src/theme/components/shared/tech-icon-table.stories.tsx +29 -0
  368. package/src/theme/components/shared/tech-icon-table.tsx +71 -0
  369. package/src/theme/components/shared/tech-tag.stories.tsx +40 -0
  370. package/src/theme/components/shared/tech-tag.tsx +31 -0
  371. package/src/theme/components/shared/theme-color-palette.css +80 -0
  372. package/src/theme/components/shared/theme-color-palette.stories.tsx +52 -0
  373. package/src/theme/components/shared/theme-color-palette.tsx +275 -0
  374. package/src/theme/components/shared/tooltip.css +69 -0
  375. package/src/theme/components/shared/tooltip.stories.tsx +72 -0
  376. package/src/theme/components/shared/tooltip.tsx +77 -0
  377. package/src/theme/components/sidebar/framework-picker.css +77 -0
  378. package/src/theme/components/sidebar/framework-picker.tsx +78 -0
  379. package/src/theme/components/sidebar/sidebar-filter.test.ts +140 -0
  380. package/src/theme/components/sidebar/sidebar-filter.ts +79 -0
  381. package/src/theme/components/sidebar/sidebar-links.css +102 -0
  382. package/src/theme/components/sidebar/sidebar-links.tsx +122 -0
  383. package/src/theme/components/sidebar/sidebar-promo.css +42 -0
  384. package/src/theme/components/sidebar/sidebar-promo.tsx +52 -0
  385. package/src/theme/components/sidebar/sidebar-scope.tsx +151 -0
  386. package/src/theme/components/sidebar/sidebar-toggle.css +181 -0
  387. package/src/theme/components/sidebar/sidebar-toggle.tsx +127 -0
  388. package/src/theme/components/theme-provider.tsx +637 -0
  389. package/src/theme/components/workspaces/card.css +151 -0
  390. package/src/theme/components/workspaces/card.tsx +166 -0
  391. package/src/theme/components/workspaces/grid.tsx +62 -0
  392. package/src/theme/css.d.ts +1 -0
  393. package/src/theme/env.d.ts +12 -0
  394. package/src/theme/fonts/GeistMono-Variable.woff2 +0 -0
  395. package/src/theme/fonts/GeistPixel-Square.woff2 +0 -0
  396. package/src/theme/fonts/GeistSans-Variable.woff2 +0 -0
  397. package/src/theme/hooks/use-ciderpress.ts +61 -0
  398. package/src/theme/hooks/use-nav-items.ts +76 -0
  399. package/src/theme/icons/index.ts +2 -0
  400. package/src/theme/icons/tech-map.ts +228 -0
  401. package/src/theme/index.tsx +180 -0
  402. package/src/theme/lib/read-social-links.ts +27 -0
  403. package/src/theme/lib/route-link.tsx +126 -0
  404. package/src/theme/lib/safe-url.test.ts +0 -0
  405. package/src/theme/lib/safe-url.ts +68 -0
  406. package/src/theme/lib/theme-favicon.ts +125 -0
  407. package/src/theme/lib/user-config-stub.ts +12 -0
  408. package/src/theme/styles/layers.css +32 -0
  409. package/src/theme/styles/overrides/badge.css +26 -0
  410. package/src/theme/styles/overrides/content-footer.css +80 -0
  411. package/src/theme/styles/overrides/details.css +61 -0
  412. package/src/theme/styles/overrides/fonts.css +32 -0
  413. package/src/theme/styles/overrides/home-card.css +133 -0
  414. package/src/theme/styles/overrides/home.css +243 -0
  415. package/src/theme/styles/overrides/rail.css +337 -0
  416. package/src/theme/styles/overrides/rspress.css +62 -0
  417. package/src/theme/styles/overrides/scrollbar.css +25 -0
  418. package/src/theme/styles/overrides/section-card.css +114 -0
  419. package/src/theme/styles/overrides/sidebar.css +144 -0
  420. package/src/theme/styles/overrides/tokens.css +288 -0
  421. package/src/theme/styles/overrides/vscode.css +88 -0
  422. package/src/theme/styles/themes/amber.css +545 -0
  423. package/src/theme/styles/themes/arcade-fx.css +366 -0
  424. package/src/theme/styles/themes/arcade.css +273 -0
  425. package/src/theme/styles/themes/grannysmith.css +545 -0
  426. package/src/theme/styles/themes/honeycrisp.css +545 -0
  427. package/src/theme/styles/themes/midnight.css +273 -0
  428. package/src/theme/styles/themes/mulled.css +817 -0
@@ -0,0 +1,31 @@
1
+ import { match, P } from 'massaman/match'
2
+ import type React from 'react'
3
+
4
+ import { TECH_ICONS } from '../../icons/index.ts'
5
+ import { Icon } from './icon'
6
+
7
+ export interface TechTagProps {
8
+ readonly name: string
9
+ }
10
+
11
+ /**
12
+ * Technology tag — resolves a tech name to icon + label using the tech map.
13
+ * Falls back to raw name string if not in map.
14
+ *
15
+ * @param props - Props with the technology name to resolve
16
+ * @returns React element with icon and label, or the raw name as fallback
17
+ */
18
+ export function TechTag({ name }: TechTagProps): React.ReactElement {
19
+ const entry = (TECH_ICONS as Record<string, { readonly icon: string; readonly label: string }>)[
20
+ name
21
+ ]
22
+
23
+ return match(entry)
24
+ .with(P.nonNullable, (e) => (
25
+ <span className="cp-tag">
26
+ <Icon icon={e.icon} />
27
+ {` ${e.label}`}
28
+ </span>
29
+ ))
30
+ .otherwise(() => <span className="cp-tag">{name}</span>)
31
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * ThemeColorPalette — grouped swatch grid rendered inside an Accordion.
3
+ */
4
+
5
+ .cp-theme-color-palette {
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: var(--cp-s-16);
9
+ padding-top: var(--cp-s-4);
10
+ }
11
+
12
+ .cp-theme-color-palette__variants {
13
+ display: inline-flex;
14
+ align-self: flex-start;
15
+ padding: var(--cp-s-4);
16
+ border: 1px solid var(--cp-c-border);
17
+ border-radius: var(--rp-radius);
18
+ background: var(--cp-c-bg);
19
+ gap: var(--cp-s-4);
20
+ }
21
+
22
+ .cp-theme-color-palette__variant {
23
+ display: inline-flex;
24
+ align-items: center;
25
+ gap: var(--cp-s-6);
26
+ padding: var(--cp-s-4) var(--cp-s-10);
27
+ border: none;
28
+ border-radius: var(--rp-radius);
29
+ background: transparent;
30
+ color: var(--cp-c-text-2);
31
+ font-family: var(--cp-font-family-base);
32
+ font-size: var(--cp-fs-color-name);
33
+ font-weight: 600;
34
+ cursor: pointer;
35
+ transition:
36
+ background var(--cp-duration-base),
37
+ color var(--cp-duration-base);
38
+ }
39
+
40
+ .cp-theme-color-palette__variant:hover {
41
+ color: var(--cp-c-text-1);
42
+ background: var(--cp-c-bg-soft);
43
+ }
44
+
45
+ .cp-theme-color-palette__variant:focus-visible {
46
+ outline: var(--cp-size-focus-ring) solid var(--cp-c-brand-1);
47
+ outline-offset: var(--cp-size-focus-ring-offset);
48
+ }
49
+
50
+ .cp-theme-color-palette__variant[data-active='true'] {
51
+ background: var(--cp-c-brand-soft);
52
+ color: var(--cp-c-brand-1);
53
+ }
54
+
55
+ .cp-theme-color-palette__variant svg {
56
+ display: block;
57
+ width: 14px;
58
+ height: 14px;
59
+ }
60
+
61
+ .cp-theme-color-palette__group {
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: var(--cp-s-8);
65
+ }
66
+
67
+ .cp-theme-color-palette__heading {
68
+ margin: 0;
69
+ font-size: var(--cp-fs-color-name);
70
+ font-weight: 600;
71
+ text-transform: uppercase;
72
+ letter-spacing: 0.06em;
73
+ color: var(--cp-c-text-3);
74
+ }
75
+
76
+ .cp-theme-color-palette__grid {
77
+ display: grid;
78
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
79
+ gap: var(--cp-s-8);
80
+ }
@@ -0,0 +1,52 @@
1
+ import type { Story } from '@ladle/react'
2
+
3
+ import { ThemeColorPalette } from './theme-color-palette.tsx'
4
+
5
+ const meta = {
6
+ title: 'Data Display / Theme Color Palette',
7
+ }
8
+
9
+ export default meta
10
+
11
+ /**
12
+ * Single palette — collapsed by default.
13
+ *
14
+ * @returns Mulled dark palette inside the accordion
15
+ */
16
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
17
+ export const Default: Story = () => <ThemeColorPalette theme="mulled" />
18
+
19
+ /**
20
+ * Palette pinned open on mount.
21
+ *
22
+ * @returns Honeycrisp dark palette, expanded
23
+ */
24
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
25
+ export const InitiallyOpen: Story = () => <ThemeColorPalette theme="honeycrisp" defaultOpen />
26
+
27
+ /**
28
+ * Explicit variant — light token tree for a multi-variant theme.
29
+ *
30
+ * @returns Honeycrisp light palette
31
+ */
32
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
33
+ export const LightVariant: Story = () => (
34
+ <ThemeColorPalette theme="honeycrisp" variant="light" defaultOpen />
35
+ )
36
+
37
+ /**
38
+ * All six built-in themes stacked, default variants, all expanded.
39
+ *
40
+ * @returns Stack of palette accordions
41
+ */
42
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
43
+ export const AllBuiltIns: Story = () => (
44
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
45
+ <ThemeColorPalette theme="mulled" defaultOpen />
46
+ <ThemeColorPalette theme="honeycrisp" />
47
+ <ThemeColorPalette theme="grannysmith" />
48
+ <ThemeColorPalette theme="amber" />
49
+ <ThemeColorPalette theme="midnight" />
50
+ <ThemeColorPalette theme="arcade" />
51
+ </div>
52
+ )
@@ -0,0 +1,275 @@
1
+ import {
2
+ BUILT_IN_THEMES,
3
+ isBuiltInTheme,
4
+ resolveDefaultVariant,
5
+ resolveThemeVariants,
6
+ } from '@ciderpress/theme'
7
+ import type { BuiltInThemeName, ThemeVariant } from '@ciderpress/theme'
8
+ import { match, P } from 'massaman/match'
9
+ import { useState } from 'react'
10
+ import type React from 'react'
11
+
12
+ import { Accordion } from './accordion.tsx'
13
+ import { Color } from './color.tsx'
14
+ import { Icon } from './icon.tsx'
15
+
16
+ export interface ThemeColorPaletteProps {
17
+ /**
18
+ * Built-in theme to render. Accepts any name in `BUILT_IN_THEMES`.
19
+ */
20
+ readonly theme: BuiltInThemeName
21
+ /**
22
+ * Initial variant to render. Defaults to the theme's `defaultVariant`.
23
+ * If the requested variant is not declared by the theme, falls back to
24
+ * the default.
25
+ */
26
+ readonly variant?: ThemeVariant
27
+ /**
28
+ * Title shown on the accordion trigger. Defaults to the theme name.
29
+ */
30
+ readonly title?: string
31
+ /**
32
+ * Description shown below the trigger title.
33
+ */
34
+ readonly description?: string
35
+ /**
36
+ * Whether the palette accordion starts expanded.
37
+ */
38
+ readonly defaultOpen?: boolean
39
+ }
40
+
41
+ interface ColorEntry {
42
+ readonly name: string
43
+ readonly value: string
44
+ }
45
+
46
+ interface ColorGroup {
47
+ readonly heading: string
48
+ readonly colors: readonly ColorEntry[]
49
+ }
50
+
51
+ const VARIANT_LABEL: Readonly<Record<ThemeVariant, string>> = Object.freeze({
52
+ dark: 'Dark',
53
+ light: 'Light',
54
+ })
55
+
56
+ const VARIANT_ICON: Readonly<Record<ThemeVariant, string>> = Object.freeze({
57
+ dark: 'pixelarticons:moon',
58
+ light: 'pixelarticons:sun',
59
+ })
60
+
61
+ /**
62
+ * Collapsible palette display for a built-in theme. Renders every brand,
63
+ * surface, text, border, and semantic color token as a copy-on-click
64
+ * {@link Color} swatch, grouped under a single {@link Accordion}. When the
65
+ * theme declares both `dark` and `light` variants, a sun/moon segmented
66
+ * control inside the panel switches between them in place.
67
+ *
68
+ * @param props - Theme palette configuration
69
+ * @returns React element with the collapsible palette
70
+ */
71
+ export function ThemeColorPalette({
72
+ theme,
73
+ variant,
74
+ title,
75
+ description,
76
+ defaultOpen = false,
77
+ }: ThemeColorPaletteProps): React.ReactElement | null {
78
+ const themeEntry = match(isBuiltInTheme(theme))
79
+ .with(true, () => BUILT_IN_THEMES[theme])
80
+ .otherwise(() => null)
81
+ if (themeEntry === null) {
82
+ return null
83
+ }
84
+
85
+ const variants = resolveThemeVariants(theme)
86
+ const initialVariant = resolveVariant(theme, variant)
87
+ const [activeVariant, setActiveVariant] = useState<ThemeVariant>(initialVariant)
88
+
89
+ const tokens = themeEntry.variants[activeVariant]
90
+ if (tokens === undefined) {
91
+ return null
92
+ }
93
+
94
+ const groups = buildGroups(tokens)
95
+ const headingText = title ?? theme
96
+ const subtitle =
97
+ description ?? `Brand, surface, text, border, and semantic tokens for the ${theme} theme.`
98
+
99
+ const showToggle = variants.length > 1
100
+
101
+ return (
102
+ <Accordion
103
+ id={`theme-palette-${theme}`}
104
+ title={headingText}
105
+ description={subtitle}
106
+ icon="pixelarticons:eyedropper"
107
+ defaultOpen={defaultOpen}
108
+ >
109
+ <div className="cp-theme-color-palette">
110
+ {match(showToggle)
111
+ .with(true, () => (
112
+ <div
113
+ className="cp-theme-color-palette__variants"
114
+ role="radiogroup"
115
+ aria-label="Variant"
116
+ >
117
+ {variants.map((v) => {
118
+ const selected = v === activeVariant
119
+ return (
120
+ <button
121
+ key={v}
122
+ type="button"
123
+ role="radio"
124
+ aria-checked={selected}
125
+ className="cp-theme-color-palette__variant"
126
+ data-active={selected}
127
+ onClick={() => setActiveVariant(v)}
128
+ >
129
+ <Icon icon={VARIANT_ICON[v]} />
130
+ <span>{VARIANT_LABEL[v]}</span>
131
+ </button>
132
+ )
133
+ })}
134
+ </div>
135
+ ))
136
+ .otherwise(() => null)}
137
+ {groups.map((group) => (
138
+ <section key={group.heading} className="cp-theme-color-palette__group">
139
+ <h4 className="cp-theme-color-palette__heading">{group.heading}</h4>
140
+ <div className="cp-theme-color-palette__grid">
141
+ {group.colors.map((c) => (
142
+ <Color key={c.name} value={c.value} name={c.name} />
143
+ ))}
144
+ </div>
145
+ </section>
146
+ ))}
147
+ </div>
148
+ </Accordion>
149
+ )
150
+ }
151
+
152
+ /**
153
+ * Resolve a variant for the requested theme — falls back to the theme's
154
+ * default when the requested variant is unavailable.
155
+ *
156
+ * @private
157
+ * @param theme - Built-in theme identifier
158
+ * @param requested - Variant supplied by the caller (optional)
159
+ * @returns Variant guaranteed to exist on the theme
160
+ */
161
+ function resolveVariant(
162
+ theme: BuiltInThemeName,
163
+ requested: ThemeVariant | undefined
164
+ ): ThemeVariant {
165
+ const fallback = resolveDefaultVariant(theme)
166
+ return match(requested)
167
+ .with(P.nullish, () => fallback)
168
+ .with(P.string, (v) => {
169
+ const exists = BUILT_IN_THEMES[theme].variants[v] !== undefined
170
+ return match(exists)
171
+ .with(true, () => v)
172
+ .otherwise(() => fallback)
173
+ })
174
+ .exhaustive()
175
+ }
176
+
177
+ /**
178
+ * Project a parsed token tree into the grouped layout rendered by the
179
+ * palette accordion.
180
+ *
181
+ * @private
182
+ * @param tokens - Token tree from `BUILT_IN_THEMES[theme].variants[variant]`
183
+ * @returns Ordered list of groups suitable for the swatch grid
184
+ */
185
+ function buildGroups(tokens: {
186
+ readonly colors: {
187
+ readonly brand: {
188
+ readonly primary: string
189
+ readonly hover: string
190
+ readonly active: string
191
+ readonly soft: string
192
+ readonly fg: string
193
+ readonly light: string
194
+ readonly lighter: string
195
+ }
196
+ readonly surface: {
197
+ readonly bg: string
198
+ readonly bgAlt: string
199
+ readonly bgElv: string
200
+ readonly bgSoft: string
201
+ readonly homeBg: string
202
+ readonly gutter: string
203
+ readonly codeBlockBg: string
204
+ }
205
+ readonly text: {
206
+ readonly text1: string
207
+ readonly text2: string
208
+ readonly text3: string
209
+ }
210
+ readonly border: {
211
+ readonly border: string
212
+ readonly divider: string
213
+ }
214
+ readonly semantic: {
215
+ readonly success: string
216
+ readonly error: string
217
+ readonly warn: string
218
+ readonly info: string
219
+ readonly muted: string
220
+ }
221
+ }
222
+ }): readonly ColorGroup[] {
223
+ const { brand, surface, text, border, semantic } = tokens.colors
224
+ return [
225
+ {
226
+ heading: 'Brand',
227
+ colors: [
228
+ { name: 'primary', value: brand.primary },
229
+ { name: 'hover', value: brand.hover },
230
+ { name: 'active', value: brand.active },
231
+ { name: 'soft', value: brand.soft },
232
+ { name: 'fg (on-brand)', value: brand.fg },
233
+ { name: 'light', value: brand.light },
234
+ { name: 'lighter', value: brand.lighter },
235
+ ],
236
+ },
237
+ {
238
+ heading: 'Surface',
239
+ colors: [
240
+ { name: 'bg', value: surface.bg },
241
+ { name: 'bgAlt', value: surface.bgAlt },
242
+ { name: 'bgElv', value: surface.bgElv },
243
+ { name: 'bgSoft', value: surface.bgSoft },
244
+ { name: 'homeBg', value: surface.homeBg },
245
+ { name: 'gutter', value: surface.gutter },
246
+ { name: 'codeBlockBg', value: surface.codeBlockBg },
247
+ ],
248
+ },
249
+ {
250
+ heading: 'Text',
251
+ colors: [
252
+ { name: 'text1', value: text.text1 },
253
+ { name: 'text2', value: text.text2 },
254
+ { name: 'text3', value: text.text3 },
255
+ ],
256
+ },
257
+ {
258
+ heading: 'Border',
259
+ colors: [
260
+ { name: 'border', value: border.border },
261
+ { name: 'divider', value: border.divider },
262
+ ],
263
+ },
264
+ {
265
+ heading: 'Semantic',
266
+ colors: [
267
+ { name: 'success', value: semantic.success },
268
+ { name: 'error', value: semantic.error },
269
+ { name: 'warn', value: semantic.warn },
270
+ { name: 'info', value: semantic.info },
271
+ { name: 'muted', value: semantic.muted },
272
+ ],
273
+ },
274
+ ]
275
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Tooltip — hover-to-reveal definitions.
3
+ */
4
+
5
+ .cp-tooltip__trigger {
6
+ display: inline;
7
+ padding: 0;
8
+ border: none;
9
+ background: none;
10
+ color: inherit;
11
+ font: inherit;
12
+ cursor: help;
13
+ text-decoration: underline;
14
+ text-decoration-style: dotted;
15
+ text-decoration-color: var(--cp-c-text-3);
16
+ text-underline-offset: var(--cp-s-3);
17
+ }
18
+
19
+ .cp-tooltip__trigger:focus-visible {
20
+ outline: var(--cp-size-focus-ring) solid var(--cp-c-brand-1);
21
+ outline-offset: var(--cp-size-focus-ring-offset);
22
+ border-radius: var(--cp-radius-xs);
23
+ }
24
+
25
+ .cp-tooltip__overlay {
26
+ max-width: var(--cp-size-tooltip-max);
27
+ padding: var(--cp-s-10) var(--cp-s-14);
28
+ background: var(--cp-c-bg-elv);
29
+ border: 1px solid var(--cp-c-border);
30
+ border-radius: var(--rp-radius-large);
31
+ box-shadow: var(--cp-shadow-tooltip);
32
+ font-size: var(--cp-fs-tooltip);
33
+ line-height: var(--cp-lh-base);
34
+ color: var(--cp-c-text-1);
35
+ z-index: var(--cp-z-tooltip);
36
+ }
37
+
38
+ .cp-tooltip__arrow svg {
39
+ display: block;
40
+ }
41
+
42
+ .cp-tooltip__body {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: var(--cp-s-4);
46
+ }
47
+
48
+ .cp-tooltip__headline {
49
+ font-size: var(--cp-fs-tooltip-headline);
50
+ font-weight: 600;
51
+ color: var(--cp-c-text-1);
52
+ }
53
+
54
+ .cp-tooltip__tip {
55
+ color: var(--cp-c-text-2);
56
+ }
57
+
58
+ .cp-tooltip__cta {
59
+ display: inline-block;
60
+ margin-top: var(--cp-s-4);
61
+ font-size: var(--cp-fs-tooltip-cta);
62
+ font-weight: 500;
63
+ color: var(--cp-c-brand-1);
64
+ text-decoration: none;
65
+ }
66
+
67
+ .cp-tooltip__cta:hover {
68
+ text-decoration: underline;
69
+ }
@@ -0,0 +1,72 @@
1
+ import type { Story } from '@ladle/react'
2
+
3
+ import { Tooltip } from './tooltip.tsx'
4
+
5
+ const meta = {
6
+ title: 'Disclosure / Tooltip',
7
+ }
8
+
9
+ export default meta
10
+
11
+ /**
12
+ * Bare tooltip — hover the trigger to reveal a tip with no headline
13
+ * or call to action.
14
+ *
15
+ * @returns Sentence with an inline tooltip trigger
16
+ */
17
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
18
+ export const Default: Story = () => (
19
+ <p style={{ lineHeight: 2 }}>
20
+ Hover{' '}
21
+ <Tooltip tip="A small package of design tokens applied as CSS custom properties to <html>.">
22
+ <span style={{ borderBottom: '1px dashed var(--cp-c-brand-1)', cursor: 'help' }}>
23
+ the theme cascade
24
+ </span>
25
+ </Tooltip>{' '}
26
+ to see the explanation pop in.
27
+ </p>
28
+ )
29
+
30
+ /**
31
+ * Tooltip with a bold headline above the tip.
32
+ *
33
+ * @returns Tooltip with headline + tip
34
+ */
35
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
36
+ export const WithHeadline: Story = () => (
37
+ <p style={{ lineHeight: 2 }}>
38
+ Read about the{' '}
39
+ <Tooltip
40
+ headline="Sync engine"
41
+ tip="Watches your source markdown and mirrors structure into Rspress's expected layout."
42
+ >
43
+ <span style={{ borderBottom: '1px dashed var(--cp-c-brand-1)', cursor: 'help' }}>
44
+ sync engine
45
+ </span>
46
+ </Tooltip>{' '}
47
+ in the architecture docs.
48
+ </p>
49
+ )
50
+
51
+ /**
52
+ * Tooltip with a call-to-action link in the overlay — routed through
53
+ * the stubbed Rspress `<Link>` so external/internal handling matches
54
+ * production.
55
+ *
56
+ * @returns Tooltip with headline, tip, and CTA
57
+ */
58
+ // oxlint-disable-next-line func-style -- Ladle stories use `export const`
59
+ export const WithCta: Story = () => (
60
+ <p style={{ lineHeight: 2 }}>
61
+ Try the{' '}
62
+ <Tooltip
63
+ headline="Mulled palette"
64
+ tip="The default Ciderpress palette — deep red brand on near-black surfaces."
65
+ cta="See the full palette →"
66
+ href="/guides/themes/mulled"
67
+ >
68
+ <span style={{ borderBottom: '1px dashed var(--cp-c-brand-1)', cursor: 'help' }}>Mulled</span>
69
+ </Tooltip>{' '}
70
+ palette.
71
+ </p>
72
+ )
@@ -0,0 +1,77 @@
1
+ import { match, P } from 'massaman/match'
2
+ import type React from 'react'
3
+ import { Button, OverlayArrow, Tooltip as AriaTooltip, TooltipTrigger } from 'react-aria-components'
4
+
5
+ import { RouteLink } from '../../lib/route-link.tsx'
6
+ import { safeUrl } from '../../lib/safe-url.ts'
7
+
8
+ export interface TooltipProps {
9
+ /**
10
+ * Main tooltip text displayed on hover.
11
+ */
12
+ readonly tip: string
13
+ /**
14
+ * Optional bold headline displayed above the tip.
15
+ */
16
+ readonly headline?: string
17
+ /**
18
+ * Optional call-to-action link label.
19
+ */
20
+ readonly cta?: string
21
+ /**
22
+ * URL for the call-to-action link. Required when `cta` is provided.
23
+ */
24
+ readonly href?: string
25
+ /**
26
+ * Inline content that triggers the tooltip on hover.
27
+ */
28
+ readonly children: React.ReactNode
29
+ }
30
+
31
+ /**
32
+ * Hover-to-reveal tooltip for definitions, glossary terms,
33
+ * and inline contextual help.
34
+ *
35
+ * Built on react-aria-components for full accessibility,
36
+ * keyboard support, and automatic repositioning.
37
+ *
38
+ * @param props - Tooltip configuration with tip text and optional CTA
39
+ * @returns React element with tooltip trigger and overlay
40
+ */
41
+ export function Tooltip({ tip, headline, cta, href, children }: TooltipProps): React.ReactElement {
42
+ const headlineEl = match(headline)
43
+ .with(P.nonNullable, (h) => <strong className="cp-tooltip__headline">{h}</strong>)
44
+ .otherwise(() => null)
45
+
46
+ const ctaEl = match({ cta, href })
47
+ .with({ cta: P.nonNullable, href: P.nonNullable }, ({ cta: label, href: url }) => {
48
+ const safeHref = safeUrl(url)
49
+ if (safeHref === null) {
50
+ return null
51
+ }
52
+ return (
53
+ <RouteLink className="cp-tooltip__cta" href={safeHref}>
54
+ {label}
55
+ </RouteLink>
56
+ )
57
+ })
58
+ .otherwise(() => null)
59
+
60
+ return (
61
+ <TooltipTrigger delay={300}>
62
+ <Button className="cp-tooltip__trigger">{children}</Button>
63
+ <AriaTooltip className="cp-tooltip__overlay" offset={8}>
64
+ <OverlayArrow className="cp-tooltip__arrow">
65
+ <svg width="12" height="6" viewBox="0 0 12 6">
66
+ <path d="M0 6L6 0L12 6" fill="var(--cp-c-bg-elv)" stroke="var(--cp-c-border)" />
67
+ </svg>
68
+ </OverlayArrow>
69
+ <div className="cp-tooltip__body">
70
+ {headlineEl}
71
+ <span className="cp-tooltip__tip">{tip}</span>
72
+ {ctaEl}
73
+ </div>
74
+ </AriaTooltip>
75
+ </TooltipTrigger>
76
+ )
77
+ }