love-ui 1.2.17 → 1.2.18

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 (413) hide show
  1. package/dist/index.js +20 -20
  2. package/dist/mcp-server.js +1 -1
  3. package/package.json +1 -1
  4. package/registry/__index__.tsx +73 -0
  5. package/registry/default/blocks/404-1/app/page.tsx +5 -0
  6. package/registry/default/blocks/404-1/components/full-width-divider.tsx +33 -0
  7. package/registry/default/blocks/404-1/components/not-found.tsx +51 -0
  8. package/registry/default/blocks/404-2/app/page.tsx +5 -0
  9. package/registry/default/blocks/404-2/components/not-found.tsx +44 -0
  10. package/registry/default/blocks/auth1/app/page.tsx +5 -0
  11. package/registry/default/blocks/auth1/components/auth-page.tsx +66 -0
  12. package/registry/default/blocks/auth1/components/icons/github-icon.tsx +10 -0
  13. package/registry/default/blocks/auth1/components/icons/google-icon.tsx +14 -0
  14. package/registry/default/blocks/auth1/components/logo.tsx +88 -0
  15. package/registry/default/blocks/auth1/components/particles.tsx +321 -0
  16. package/registry/default/blocks/auth2/app/page.tsx +5 -0
  17. package/registry/default/blocks/auth2/components/auth.tsx +87 -0
  18. package/registry/default/blocks/auth2/components/icons/github-icon.tsx +10 -0
  19. package/registry/default/blocks/auth2/components/icons/google-icon.tsx +14 -0
  20. package/registry/default/blocks/auth2/components/ui/auth-divider.tsx +16 -0
  21. package/registry/default/blocks/auth2/components/ui/decor-icon.tsx +45 -0
  22. package/registry/default/blocks/auth3/app/page.tsx +5 -0
  23. package/registry/default/blocks/auth3/components/auth-page.tsx +84 -0
  24. package/registry/default/blocks/auth3/components/icons/github-icon.tsx +10 -0
  25. package/registry/default/blocks/auth3/components/icons/google-icon.tsx +14 -0
  26. package/registry/default/blocks/auth3/components/logo.tsx +88 -0
  27. package/registry/default/blocks/auth3/components/ui/auth-divider.tsx +16 -0
  28. package/registry/default/blocks/auth3/components/ui/decor-icon.tsx +45 -0
  29. package/registry/default/blocks/blogs1/app/page.tsx +5 -0
  30. package/registry/default/blocks/blogs1/components/blogs.tsx +97 -0
  31. package/registry/default/blocks/blogs1/components/full-width-divider.tsx +33 -0
  32. package/registry/default/blocks/blogs2/app/page.tsx +5 -0
  33. package/registry/default/blocks/blogs2/components/blogs.tsx +158 -0
  34. package/registry/default/blocks/blogs2/components/full-width-divider.tsx +33 -0
  35. package/registry/default/blocks/blogs2/components/grid-filler.tsx +74 -0
  36. package/registry/default/blocks/blogs3/app/page.tsx +5 -0
  37. package/registry/default/blocks/blogs3/components/aspect-ratio.tsx +22 -0
  38. package/registry/default/blocks/blogs3/components/blogs.tsx +224 -0
  39. package/registry/default/blocks/blogs3/components/full-width-divider.tsx +33 -0
  40. package/registry/default/blocks/blogs3/components/lazy-image.tsx +94 -0
  41. package/registry/default/blocks/contact1/app/page.tsx +9 -0
  42. package/registry/default/blocks/contact1/components/contact.tsx +65 -0
  43. package/registry/default/blocks/contact1/components/full-width-divider.tsx +33 -0
  44. package/registry/default/blocks/contact2/app/page.tsx +9 -0
  45. package/registry/default/blocks/contact2/components/contact.tsx +74 -0
  46. package/registry/default/blocks/contact2/components/full-width-divider.tsx +33 -0
  47. package/registry/default/blocks/contact2/components/icons/x-icon.tsx +12 -0
  48. package/registry/default/blocks/contact3/app/page.tsx +9 -0
  49. package/registry/default/blocks/contact3/components/contact.tsx +152 -0
  50. package/registry/default/blocks/contact3/components/full-width-divider.tsx +33 -0
  51. package/registry/default/blocks/contact3/components/icons/github-icon.tsx +10 -0
  52. package/registry/default/blocks/contact3/components/icons/x-icon.tsx +12 -0
  53. package/registry/default/blocks/contact4/app/page.tsx +9 -0
  54. package/registry/default/blocks/contact4/components/contact.tsx +120 -0
  55. package/registry/default/blocks/contact5/app/page.tsx +9 -0
  56. package/registry/default/blocks/contact5/components/contact.tsx +119 -0
  57. package/registry/default/blocks/contact5/components/decor-icon.tsx +45 -0
  58. package/registry/default/blocks/cta-1/app/page.tsx +9 -0
  59. package/registry/default/blocks/cta-1/components/cta.tsx +20 -0
  60. package/registry/default/blocks/cta-1/components/full-width-divider.tsx +33 -0
  61. package/registry/default/blocks/cta-2/app/page.tsx +9 -0
  62. package/registry/default/blocks/cta-2/components/cta.tsx +27 -0
  63. package/registry/default/blocks/cta-2/components/full-width-divider.tsx +33 -0
  64. package/registry/default/blocks/cta-3/app/page.tsx +9 -0
  65. package/registry/default/blocks/cta-3/components/cta.tsx +35 -0
  66. package/registry/default/blocks/cta-3/components/decor-icon.tsx +45 -0
  67. package/registry/default/blocks/cta-4/app/page.tsx +9 -0
  68. package/registry/default/blocks/cta-4/components/cta.tsx +28 -0
  69. package/registry/default/blocks/cta-5/app/page.tsx +9 -0
  70. package/registry/default/blocks/cta-5/components/cta.tsx +72 -0
  71. package/registry/default/blocks/cta-5/components/full-width-divider.tsx +33 -0
  72. package/registry/default/blocks/faq-1/app/page.tsx +9 -0
  73. package/registry/default/blocks/faq-1/components/faq.tsx +86 -0
  74. package/registry/default/blocks/faq-2/app/page.tsx +9 -0
  75. package/registry/default/blocks/faq-2/components/faq.tsx +93 -0
  76. package/registry/default/blocks/faq-3/app/page.tsx +9 -0
  77. package/registry/default/blocks/faq-3/components/decor-icon.tsx +45 -0
  78. package/registry/default/blocks/faq-3/components/faq.tsx +110 -0
  79. package/registry/default/blocks/faq-4/app/page.tsx +9 -0
  80. package/registry/default/blocks/faq-4/components/faq.tsx +181 -0
  81. package/registry/default/blocks/faq-5/app/page.tsx +9 -0
  82. package/registry/default/blocks/faq-5/components/faq.tsx +211 -0
  83. package/registry/default/blocks/faq-5/components/full-width-divider.tsx +33 -0
  84. package/registry/default/blocks/features1/app/page.tsx +9 -0
  85. package/registry/default/blocks/features1/components/feature-section.tsx +73 -0
  86. package/registry/default/blocks/features2/app/page.tsx +9 -0
  87. package/registry/default/blocks/features2/components/decor-icon.tsx +45 -0
  88. package/registry/default/blocks/features2/components/feature-section.tsx +97 -0
  89. package/registry/default/blocks/features3/app/page.tsx +9 -0
  90. package/registry/default/blocks/features3/components/feature-section.tsx +93 -0
  91. package/registry/default/blocks/features3/components/full-width-divider.tsx +21 -0
  92. package/registry/default/blocks/features4/app/page.tsx +9 -0
  93. package/registry/default/blocks/features4/components/decor-icon.tsx +45 -0
  94. package/registry/default/blocks/features4/components/feature-section.tsx +114 -0
  95. package/registry/default/blocks/features5/app/page.tsx +9 -0
  96. package/registry/default/blocks/features5/components/feature-section.tsx +115 -0
  97. package/registry/default/blocks/features5/components/grid-pattern.tsx +70 -0
  98. package/registry/default/blocks/features6/app/page.tsx +9 -0
  99. package/registry/default/blocks/features6/components/cobe-globe.tsx +86 -0
  100. package/registry/default/blocks/features6/components/feature-section.tsx +309 -0
  101. package/registry/default/blocks/footer1/app/page.tsx +9 -0
  102. package/registry/default/blocks/footer1/components/footer.tsx +85 -0
  103. package/registry/default/blocks/footer1/components/icons/github-icon.tsx +10 -0
  104. package/registry/default/blocks/footer1/components/icons/x-icon.tsx +12 -0
  105. package/registry/default/blocks/footer1/components/logo.tsx +88 -0
  106. package/registry/default/blocks/footer2/app/page.tsx +9 -0
  107. package/registry/default/blocks/footer2/components/footer.tsx +139 -0
  108. package/registry/default/blocks/footer2/components/full-width-divider.tsx +21 -0
  109. package/registry/default/blocks/footer2/components/icons/github-icon.tsx +10 -0
  110. package/registry/default/blocks/footer2/components/icons/instagram-icon.tsx +12 -0
  111. package/registry/default/blocks/footer2/components/icons/x-icon.tsx +12 -0
  112. package/registry/default/blocks/footer2/components/logo.tsx +88 -0
  113. package/registry/default/blocks/footer3/app/page.tsx +9 -0
  114. package/registry/default/blocks/footer3/components/footer.tsx +155 -0
  115. package/registry/default/blocks/footer3/components/icons/github-icon.tsx +10 -0
  116. package/registry/default/blocks/footer3/components/icons/instagram-icon.tsx +12 -0
  117. package/registry/default/blocks/footer3/components/icons/linkedin-icon.tsx +12 -0
  118. package/registry/default/blocks/footer3/components/icons/x-icon.tsx +12 -0
  119. package/registry/default/blocks/footer3/components/logo.tsx +88 -0
  120. package/registry/default/blocks/footer4/app/page.tsx +9 -0
  121. package/registry/default/blocks/footer4/components/footer.tsx +137 -0
  122. package/registry/default/blocks/footer4/components/icons/github-icon.tsx +10 -0
  123. package/registry/default/blocks/footer4/components/icons/instagram-icon.tsx +12 -0
  124. package/registry/default/blocks/footer4/components/icons/linkedin-icon.tsx +12 -0
  125. package/registry/default/blocks/footer4/components/icons/x-icon.tsx +12 -0
  126. package/registry/default/blocks/footer4/components/logo.tsx +88 -0
  127. package/registry/default/blocks/footer5/app/page.tsx +9 -0
  128. package/registry/default/blocks/footer5/components/footer.tsx +152 -0
  129. package/registry/default/blocks/footer5/components/icons/apple-icon.tsx +18 -0
  130. package/registry/default/blocks/footer5/components/icons/facebook-icon.tsx +12 -0
  131. package/registry/default/blocks/footer5/components/icons/google-play-icon.tsx +10 -0
  132. package/registry/default/blocks/footer5/components/icons/instagram-icon.tsx +12 -0
  133. package/registry/default/blocks/footer5/components/icons/linkedin-icon.tsx +12 -0
  134. package/registry/default/blocks/footer5/components/icons/x-icon.tsx +12 -0
  135. package/registry/default/blocks/footer5/components/logo.tsx +88 -0
  136. package/registry/default/blocks/footer6/app/page.tsx +16 -0
  137. package/registry/default/blocks/footer6/components/footer.tsx +220 -0
  138. package/registry/default/blocks/footer6/components/icons/apple-icon.tsx +18 -0
  139. package/registry/default/blocks/footer6/components/icons/facebook-icon.tsx +12 -0
  140. package/registry/default/blocks/footer6/components/icons/github-icon.tsx +10 -0
  141. package/registry/default/blocks/footer6/components/icons/google-play-icon.tsx +10 -0
  142. package/registry/default/blocks/footer6/components/icons/instagram-icon.tsx +12 -0
  143. package/registry/default/blocks/footer6/components/icons/linkedin-icon.tsx +12 -0
  144. package/registry/default/blocks/footer6/components/icons/x-icon.tsx +12 -0
  145. package/registry/default/blocks/footer6/components/logo.tsx +88 -0
  146. package/registry/default/blocks/header1/app/page.tsx +12 -0
  147. package/registry/default/blocks/header1/components/demo-layout.tsx +33 -0
  148. package/registry/default/blocks/header1/components/header.tsx +58 -0
  149. package/registry/default/blocks/header1/components/logo.tsx +88 -0
  150. package/registry/default/blocks/header1/components/mobile-nav.tsx +61 -0
  151. package/registry/default/blocks/header1/components/ui/portal.tsx +27 -0
  152. package/registry/default/blocks/header1/hooks/use-scroll.ts +54 -0
  153. package/registry/default/blocks/header2/app/page.tsx +11 -0
  154. package/registry/default/blocks/header2/components/demo-layout.tsx +33 -0
  155. package/registry/default/blocks/header2/components/header.tsx +70 -0
  156. package/registry/default/blocks/header2/components/logo.tsx +88 -0
  157. package/registry/default/blocks/header2/components/mobile-nav.tsx +61 -0
  158. package/registry/default/blocks/header2/components/ui/portal.tsx +27 -0
  159. package/registry/default/blocks/header2/hooks/use-scroll.ts +54 -0
  160. package/registry/default/blocks/header3/app/page.tsx +11 -0
  161. package/registry/default/blocks/header3/components/demo-layout.tsx +33 -0
  162. package/registry/default/blocks/header3/components/desktop-nav.tsx +70 -0
  163. package/registry/default/blocks/header3/components/header.tsx +41 -0
  164. package/registry/default/blocks/header3/components/logo.tsx +88 -0
  165. package/registry/default/blocks/header3/components/mobile-nav.tsx +88 -0
  166. package/registry/default/blocks/header3/components/nav-links.tsx +132 -0
  167. package/registry/default/blocks/header3/components/sheard.tsx +41 -0
  168. package/registry/default/blocks/header3/components/ui/navigation-menu.tsx +171 -0
  169. package/registry/default/blocks/header3/components/ui/portal.tsx +27 -0
  170. package/registry/default/blocks/header3/hooks/use-scroll.ts +54 -0
  171. package/registry/default/blocks/hero1/app/page.tsx +15 -0
  172. package/registry/default/blocks/hero1/components/header.tsx +70 -0
  173. package/registry/default/blocks/hero1/components/hero.tsx +80 -0
  174. package/registry/default/blocks/hero1/components/infinite-slider.tsx +109 -0
  175. package/registry/default/blocks/hero1/components/logo-cloud.tsx +69 -0
  176. package/registry/default/blocks/hero1/components/logo.tsx +88 -0
  177. package/registry/default/blocks/hero1/components/logos/claude-wordmark.svg +1 -0
  178. package/registry/default/blocks/hero1/components/logos/clerk-wordmark.svg +1 -0
  179. package/registry/default/blocks/hero1/components/logos/github-wordmark.svg +6 -0
  180. package/registry/default/blocks/hero1/components/logos/nvidia-wordmark.svg +1 -0
  181. package/registry/default/blocks/hero1/components/logos/openai-wordmark.svg +1 -0
  182. package/registry/default/blocks/hero1/components/logos/supabase-wordmark.svg +23 -0
  183. package/registry/default/blocks/hero1/components/logos/turso-wordmark.svg +1 -0
  184. package/registry/default/blocks/hero1/components/logos/vercel-wordmark.svg +1 -0
  185. package/registry/default/blocks/hero1/components/logos-section.tsx +14 -0
  186. package/registry/default/blocks/hero1/components/mobile-nav.tsx +61 -0
  187. package/registry/default/blocks/hero1/components/ui/portal.tsx +27 -0
  188. package/registry/default/blocks/hero1/hooks/use-scroll.ts +54 -0
  189. package/registry/default/blocks/hero2/app/page.tsx +23 -0
  190. package/registry/default/blocks/hero2/components/decor-icon.tsx +45 -0
  191. package/registry/default/blocks/hero2/components/full-width-divider.tsx +21 -0
  192. package/registry/default/blocks/hero2/components/header.tsx +70 -0
  193. package/registry/default/blocks/hero2/components/hero.tsx +104 -0
  194. package/registry/default/blocks/hero2/components/infinite-slider.tsx +109 -0
  195. package/registry/default/blocks/hero2/components/logo-cloud.tsx +120 -0
  196. package/registry/default/blocks/hero2/components/logo.tsx +88 -0
  197. package/registry/default/blocks/hero2/components/logos/claude-wordmark.svg +1 -0
  198. package/registry/default/blocks/hero2/components/logos/clerk-wordmark.svg +1 -0
  199. package/registry/default/blocks/hero2/components/logos/github-wordmark.svg +6 -0
  200. package/registry/default/blocks/hero2/components/logos/nvidia-wordmark.svg +1 -0
  201. package/registry/default/blocks/hero2/components/logos/openai-wordmark.svg +1 -0
  202. package/registry/default/blocks/hero2/components/logos/supabase-wordmark.svg +23 -0
  203. package/registry/default/blocks/hero2/components/logos/turso-wordmark.svg +1 -0
  204. package/registry/default/blocks/hero2/components/logos/vercel-wordmark.svg +1 -0
  205. package/registry/default/blocks/hero2/components/logos-section.tsx +23 -0
  206. package/registry/default/blocks/hero2/components/mobile-nav.tsx +61 -0
  207. package/registry/default/blocks/hero2/components/ui/portal.tsx +27 -0
  208. package/registry/default/blocks/hero2/hooks/use-scroll.ts +54 -0
  209. package/registry/default/blocks/hero3/app/page.tsx +15 -0
  210. package/registry/default/blocks/hero3/components/header.tsx +70 -0
  211. package/registry/default/blocks/hero3/components/hero.tsx +104 -0
  212. package/registry/default/blocks/hero3/components/infinite-slider.tsx +109 -0
  213. package/registry/default/blocks/hero3/components/logo-cloud.tsx +67 -0
  214. package/registry/default/blocks/hero3/components/logo.tsx +88 -0
  215. package/registry/default/blocks/hero3/components/logos/claude-wordmark.svg +1 -0
  216. package/registry/default/blocks/hero3/components/logos/clerk-wordmark.svg +1 -0
  217. package/registry/default/blocks/hero3/components/logos/github-wordmark.svg +6 -0
  218. package/registry/default/blocks/hero3/components/logos/nvidia-wordmark.svg +1 -0
  219. package/registry/default/blocks/hero3/components/logos/openai-wordmark.svg +1 -0
  220. package/registry/default/blocks/hero3/components/logos/supabase-wordmark.svg +23 -0
  221. package/registry/default/blocks/hero3/components/logos/turso-wordmark.svg +1 -0
  222. package/registry/default/blocks/hero3/components/logos/vercel-wordmark.svg +1 -0
  223. package/registry/default/blocks/hero3/components/logos-section.tsx +12 -0
  224. package/registry/default/blocks/hero3/components/mobile-nav.tsx +61 -0
  225. package/registry/default/blocks/hero3/components/ui/portal.tsx +27 -0
  226. package/registry/default/blocks/hero3/hooks/use-scroll.ts +54 -0
  227. package/registry/default/blocks/image-gallery-1/app/page.tsx +5 -0
  228. package/registry/default/blocks/image-gallery-1/components/aspect-ratio.tsx +22 -0
  229. package/registry/default/blocks/image-gallery-1/components/image-gallery.tsx +135 -0
  230. package/registry/default/blocks/image-gallery-1/components/lazy-image.tsx +94 -0
  231. package/registry/default/blocks/integrations1/app/page.tsx +9 -0
  232. package/registry/default/blocks/integrations1/components/integrations.tsx +86 -0
  233. package/registry/default/blocks/integrations1/components/logos/adobe.svg +1 -0
  234. package/registry/default/blocks/integrations1/components/logos/canva.svg +1 -0
  235. package/registry/default/blocks/integrations1/components/logos/cursor.svg +1 -0
  236. package/registry/default/blocks/integrations1/components/logos/gmail.svg +1 -0
  237. package/registry/default/blocks/integrations1/components/logos/notion.svg +1 -0
  238. package/registry/default/blocks/integrations1/components/logos/planetscale.svg +1 -0
  239. package/registry/default/blocks/integrations1/components/logos/polar.svg +1 -0
  240. package/registry/default/blocks/integrations1/components/logos/supabase.svg +1 -0
  241. package/registry/default/blocks/integrations1/components/logos/vercel.svg +1 -0
  242. package/registry/default/blocks/integrations2/app/page.tsx +9 -0
  243. package/registry/default/blocks/integrations2/components/decor-icon.tsx +45 -0
  244. package/registry/default/blocks/integrations2/components/integrations.tsx +118 -0
  245. package/registry/default/blocks/integrations2/components/logos/adobe.svg +1 -0
  246. package/registry/default/blocks/integrations2/components/logos/canva.svg +1 -0
  247. package/registry/default/blocks/integrations2/components/logos/cursor.svg +1 -0
  248. package/registry/default/blocks/integrations2/components/logos/gmail.svg +1 -0
  249. package/registry/default/blocks/integrations2/components/logos/notion.svg +1 -0
  250. package/registry/default/blocks/integrations2/components/logos/planetscale.svg +1 -0
  251. package/registry/default/blocks/integrations2/components/logos/polar.svg +1 -0
  252. package/registry/default/blocks/integrations2/components/logos/supabase.svg +1 -0
  253. package/registry/default/blocks/integrations2/components/logos/vercel.svg +1 -0
  254. package/registry/default/blocks/integrations3/app/page.tsx +9 -0
  255. package/registry/default/blocks/integrations3/components/full-width-divider.tsx +33 -0
  256. package/registry/default/blocks/integrations3/components/integrations.tsx +190 -0
  257. package/registry/default/blocks/integrations3/components/logos/adobe.svg +1 -0
  258. package/registry/default/blocks/integrations3/components/logos/canva.svg +1 -0
  259. package/registry/default/blocks/integrations3/components/logos/cursor.svg +1 -0
  260. package/registry/default/blocks/integrations3/components/logos/gmail.svg +1 -0
  261. package/registry/default/blocks/integrations3/components/logos/notion.svg +1 -0
  262. package/registry/default/blocks/integrations3/components/logos/planetscale.svg +1 -0
  263. package/registry/default/blocks/integrations3/components/logos/polar.svg +1 -0
  264. package/registry/default/blocks/integrations3/components/logos/supabase.svg +1 -0
  265. package/registry/default/blocks/integrations3/components/logos/vercel.svg +1 -0
  266. package/registry/default/blocks/integrations4/app/page.tsx +9 -0
  267. package/registry/default/blocks/integrations4/components/integrations.tsx +177 -0
  268. package/registry/default/blocks/integrations4/components/logos/adobe.svg +1 -0
  269. package/registry/default/blocks/integrations4/components/logos/canva.svg +1 -0
  270. package/registry/default/blocks/integrations4/components/logos/cursor.svg +1 -0
  271. package/registry/default/blocks/integrations4/components/logos/gmail.svg +1 -0
  272. package/registry/default/blocks/integrations4/components/logos/notion.svg +1 -0
  273. package/registry/default/blocks/integrations4/components/logos/planetscale.svg +1 -0
  274. package/registry/default/blocks/integrations4/components/logos/polar.svg +1 -0
  275. package/registry/default/blocks/integrations4/components/logos/supabase.svg +1 -0
  276. package/registry/default/blocks/integrations4/components/logos/vercel.svg +1 -0
  277. package/registry/default/blocks/integrations5/app/page.tsx +9 -0
  278. package/registry/default/blocks/integrations5/components/integrations.tsx +112 -0
  279. package/registry/default/blocks/integrations5/components/logos/adobe.svg +1 -0
  280. package/registry/default/blocks/integrations5/components/logos/canva.svg +1 -0
  281. package/registry/default/blocks/integrations5/components/logos/cursor.svg +1 -0
  282. package/registry/default/blocks/integrations5/components/logos/gmail.svg +1 -0
  283. package/registry/default/blocks/integrations5/components/logos/notion.svg +1 -0
  284. package/registry/default/blocks/integrations5/components/logos/planetscale.svg +1 -0
  285. package/registry/default/blocks/integrations5/components/logos/polar.svg +1 -0
  286. package/registry/default/blocks/integrations5/components/logos/supabase.svg +1 -0
  287. package/registry/default/blocks/integrations5/components/logos/vercel.svg +1 -0
  288. package/registry/default/blocks/logo-cloud-1/app/page.tsx +17 -0
  289. package/registry/default/blocks/logo-cloud-1/components/logo-cloud.tsx +70 -0
  290. package/registry/default/blocks/logo-cloud-1/components/logos/claude-wordmark.svg +1 -0
  291. package/registry/default/blocks/logo-cloud-1/components/logos/clerk-wordmark.svg +1 -0
  292. package/registry/default/blocks/logo-cloud-1/components/logos/github-wordmark.svg +6 -0
  293. package/registry/default/blocks/logo-cloud-1/components/logos/nvidia-wordmark.svg +1 -0
  294. package/registry/default/blocks/logo-cloud-1/components/logos/openai-wordmark.svg +1 -0
  295. package/registry/default/blocks/logo-cloud-1/components/logos/supabase-wordmark.svg +23 -0
  296. package/registry/default/blocks/logo-cloud-1/components/logos/turso-wordmark.svg +1 -0
  297. package/registry/default/blocks/logo-cloud-1/components/logos/vercel-wordmark.svg +1 -0
  298. package/registry/default/blocks/logo-cloud-2/app/page.tsx +19 -0
  299. package/registry/default/blocks/logo-cloud-2/components/decor-icon.tsx +45 -0
  300. package/registry/default/blocks/logo-cloud-2/components/logo-cloud.tsx +120 -0
  301. package/registry/default/blocks/logo-cloud-2/components/logos/claude-wordmark.svg +1 -0
  302. package/registry/default/blocks/logo-cloud-2/components/logos/clerk-wordmark.svg +1 -0
  303. package/registry/default/blocks/logo-cloud-2/components/logos/github-wordmark.svg +6 -0
  304. package/registry/default/blocks/logo-cloud-2/components/logos/nvidia-wordmark.svg +1 -0
  305. package/registry/default/blocks/logo-cloud-2/components/logos/openai-wordmark.svg +1 -0
  306. package/registry/default/blocks/logo-cloud-2/components/logos/supabase-wordmark.svg +23 -0
  307. package/registry/default/blocks/logo-cloud-2/components/logos/turso-wordmark.svg +1 -0
  308. package/registry/default/blocks/logo-cloud-2/components/logos/vercel-wordmark.svg +1 -0
  309. package/registry/default/blocks/logo-cloud-3/app/page.tsx +18 -0
  310. package/registry/default/blocks/logo-cloud-3/components/infinite-slider.tsx +109 -0
  311. package/registry/default/blocks/logo-cloud-3/components/logo-cloud.tsx +69 -0
  312. package/registry/default/blocks/logo-cloud-3/components/logos/claude-wordmark.svg +1 -0
  313. package/registry/default/blocks/logo-cloud-3/components/logos/clerk-wordmark.svg +1 -0
  314. package/registry/default/blocks/logo-cloud-3/components/logos/github-wordmark.svg +6 -0
  315. package/registry/default/blocks/logo-cloud-3/components/logos/nvidia-wordmark.svg +1 -0
  316. package/registry/default/blocks/logo-cloud-3/components/logos/openai-wordmark.svg +1 -0
  317. package/registry/default/blocks/logo-cloud-3/components/logos/supabase-wordmark.svg +23 -0
  318. package/registry/default/blocks/logo-cloud-3/components/logos/turso-wordmark.svg +1 -0
  319. package/registry/default/blocks/logo-cloud-3/components/logos/vercel-wordmark.svg +1 -0
  320. package/registry/default/blocks/logo-cloud-4/app/page.tsx +23 -0
  321. package/registry/default/blocks/logo-cloud-4/components/infinite-slider.tsx +109 -0
  322. package/registry/default/blocks/logo-cloud-4/components/logo-cloud.tsx +83 -0
  323. package/registry/default/blocks/logo-cloud-4/components/logos/claude-wordmark.svg +1 -0
  324. package/registry/default/blocks/logo-cloud-4/components/logos/clerk-wordmark.svg +1 -0
  325. package/registry/default/blocks/logo-cloud-4/components/logos/github-wordmark.svg +6 -0
  326. package/registry/default/blocks/logo-cloud-4/components/logos/nvidia-wordmark.svg +1 -0
  327. package/registry/default/blocks/logo-cloud-4/components/logos/openai-wordmark.svg +1 -0
  328. package/registry/default/blocks/logo-cloud-4/components/logos/supabase-wordmark.svg +23 -0
  329. package/registry/default/blocks/logo-cloud-4/components/logos/turso-wordmark.svg +1 -0
  330. package/registry/default/blocks/logo-cloud-4/components/logos/vercel-wordmark.svg +1 -0
  331. package/registry/default/blocks/logo-cloud-4/components/progressive-blur.tsx +63 -0
  332. package/registry/default/blocks/logo-cloud-5/app/page.tsx +17 -0
  333. package/registry/default/blocks/logo-cloud-5/components/logo-cloud.tsx +67 -0
  334. package/registry/default/blocks/logo-cloud-5/components/logos/claude-wordmark.svg +1 -0
  335. package/registry/default/blocks/logo-cloud-5/components/logos/clerk-wordmark.svg +1 -0
  336. package/registry/default/blocks/logo-cloud-5/components/logos/github-wordmark.svg +6 -0
  337. package/registry/default/blocks/logo-cloud-5/components/logos/nvidia-wordmark.svg +1 -0
  338. package/registry/default/blocks/logo-cloud-5/components/logos/openai-wordmark.svg +1 -0
  339. package/registry/default/blocks/logo-cloud-5/components/logos/supabase-wordmark.svg +23 -0
  340. package/registry/default/blocks/logo-cloud-5/components/logos/turso-wordmark.svg +1 -0
  341. package/registry/default/blocks/logo-cloud-5/components/logos/vercel-wordmark.svg +1 -0
  342. package/registry/default/blocks/pricing1/app/page.tsx +9 -0
  343. package/registry/default/blocks/pricing1/components/decor-icon.tsx +45 -0
  344. package/registry/default/blocks/pricing1/components/pricing.tsx +94 -0
  345. package/registry/default/blocks/pricing2/app/page.tsx +9 -0
  346. package/registry/default/blocks/pricing2/components/full-width-divider.tsx +33 -0
  347. package/registry/default/blocks/pricing2/components/pricing.tsx +117 -0
  348. package/registry/default/blocks/pricing3/app/page.tsx +9 -0
  349. package/registry/default/blocks/pricing3/components/pricing-card.tsx +171 -0
  350. package/registry/default/blocks/pricing3/components/pricing.tsx +143 -0
  351. package/registry/default/blocks/pricing4/app/page.tsx +9 -0
  352. package/registry/default/blocks/pricing4/components/frequency-toggle.tsx +47 -0
  353. package/registry/default/blocks/pricing4/components/pricing.tsx +230 -0
  354. package/registry/default/blocks/testimonials1/app/page.tsx +9 -0
  355. package/registry/default/blocks/testimonials1/components/logo.tsx +74 -0
  356. package/registry/default/blocks/testimonials1/components/testimonials.tsx +41 -0
  357. package/registry/default/blocks/testimonials2/app/page.tsx +9 -0
  358. package/registry/default/blocks/testimonials2/components/testimonials.tsx +65 -0
  359. package/registry/default/blocks/testimonials3/app/page.tsx +9 -0
  360. package/registry/default/blocks/testimonials3/components/decor-icon.tsx +45 -0
  361. package/registry/default/blocks/testimonials3/components/testimonials.tsx +114 -0
  362. package/registry/default/blocks/testimonials4/app/page.tsx +9 -0
  363. package/registry/default/blocks/testimonials4/components/full-width-divider.tsx +33 -0
  364. package/registry/default/blocks/testimonials4/components/testimonials.tsx +81 -0
  365. package/registry/default/blocks/testimonials5/app/page.tsx +9 -0
  366. package/registry/default/blocks/testimonials5/components/full-width-divider.tsx +33 -0
  367. package/registry/default/blocks/testimonials5/components/grid-filler.tsx +74 -0
  368. package/registry/default/blocks/testimonials5/components/grid-pattern.tsx +70 -0
  369. package/registry/default/blocks/testimonials5/components/testimonials.tsx +186 -0
  370. package/registry/default/blocks/testimonials6/app/page.tsx +9 -0
  371. package/registry/default/blocks/testimonials6/components/infinite-slider.tsx +109 -0
  372. package/registry/default/blocks/testimonials6/components/testimonials.tsx +193 -0
  373. package/registry/default/examples/accordion-disabled.tsx +58 -0
  374. package/registry/default/examples/accordion-in-card.tsx +92 -0
  375. package/registry/default/examples/accordion-leading-icon.tsx +51 -0
  376. package/registry/default/examples/accordion-user-roles.tsx +97 -0
  377. package/registry/default/examples/accordion-with-icons.tsx +67 -0
  378. package/registry/default/examples/avatar-badge-icons.tsx +60 -0
  379. package/registry/default/examples/avatar-badge-position.tsx +47 -0
  380. package/registry/default/examples/avatar-empty-collaborators.tsx +55 -0
  381. package/registry/default/examples/avatar-group-trust.tsx +49 -0
  382. package/registry/default/examples/avatar-loading.tsx +33 -0
  383. package/registry/default/examples/avatar-menu.tsx +81 -0
  384. package/registry/default/examples/avatar-profile-badge.tsx +30 -0
  385. package/registry/default/examples/avatar-status.tsx +47 -0
  386. package/registry/default/examples/breadcrumb-bullet-separator.tsx +38 -0
  387. package/registry/default/examples/breadcrumb-buttons.tsx +61 -0
  388. package/registry/default/examples/breadcrumb-card.tsx +40 -0
  389. package/registry/default/examples/button-default-icons.tsx +18 -0
  390. package/registry/default/examples/button-default.tsx +5 -0
  391. package/registry/default/examples/button-demo.tsx +11 -1
  392. package/registry/default/examples/button-destructive-icons.tsx +18 -0
  393. package/registry/default/examples/button-destructive-outline-icons.tsx +18 -0
  394. package/registry/default/examples/button-ghost-icons.tsx +18 -0
  395. package/registry/default/examples/button-link-icons.tsx +18 -0
  396. package/registry/default/examples/button-outline-icons.tsx +18 -0
  397. package/registry/default/examples/button-secondary-icons.tsx +18 -0
  398. package/registry/default/examples/button-theme-toggle.tsx +38 -0
  399. package/registry/default/examples/card-author-profile.tsx +66 -0
  400. package/registry/default/examples/card-default-size.tsx +33 -0
  401. package/registry/default/examples/card-depth.tsx +37 -0
  402. package/registry/default/examples/card-help-link.tsx +32 -0
  403. package/registry/default/examples/card-help-menu.tsx +78 -0
  404. package/registry/default/examples/card-image-centered.tsx +38 -0
  405. package/registry/default/examples/card-image-feature.tsx +44 -0
  406. package/registry/default/examples/card-metric-actions.tsx +102 -0
  407. package/registry/default/examples/card-resource-link.tsx +40 -0
  408. package/registry/default/examples/card-team-member.tsx +38 -14
  409. package/registry/default/examples/card-usage-expandable.tsx +98 -0
  410. package/registry/default/examples/card-with-borders.tsx +29 -0
  411. package/registry/default/ui/accordion.tsx +82 -2
  412. package/registry/default/ui/avatar.tsx +45 -1
  413. package/registry/default/ui/input-group.tsx +158 -0
@@ -0,0 +1,74 @@
1
+ import { cn } from "@/lib/utils";
2
+
3
+ type GridFillerProps = React.ComponentProps<"div"> & {
4
+ /**
5
+ * The number of items in the grid.
6
+ */
7
+ totalItems: number;
8
+ /**
9
+ * Number of columns for small screens.
10
+ */
11
+ smColumns?: number;
12
+ /**
13
+ * Number of columns for medium screens.
14
+ */
15
+ mdColumns?: number;
16
+ /**
17
+ * Number of columns for large screens.
18
+ */
19
+ lgColumns?: number;
20
+ };
21
+
22
+ export function GridFiller({
23
+ totalItems,
24
+ className,
25
+ smColumns = 2,
26
+ mdColumns,
27
+ lgColumns,
28
+ ...props
29
+ }: GridFillerProps) {
30
+ const actualMdColumns = mdColumns ?? smColumns;
31
+ const actualLgColumns = lgColumns ?? actualMdColumns;
32
+
33
+ // We need enough iterations to cover the maximum possible remainder.
34
+ // For N columns, the remainder can be at most N-1.
35
+ const maxFillers = Math.max(smColumns, actualMdColumns, actualLgColumns) - 1;
36
+
37
+ return (
38
+ <>
39
+ {Array.from({ length: maxFillers }).map((_, i) => {
40
+ const neededSm = (smColumns - (totalItems % smColumns)) % smColumns;
41
+ const neededMd =
42
+ (actualMdColumns - (totalItems % actualMdColumns)) % actualMdColumns;
43
+ const neededLg =
44
+ (actualLgColumns - (totalItems % actualLgColumns)) % actualLgColumns;
45
+
46
+ const showSm = i < neededSm ? "sm:block" : "sm:hidden";
47
+ const showMd = i < neededMd ? "md:block" : "md:hidden";
48
+ const showLg = i < neededLg ? "lg:block" : "lg:hidden";
49
+
50
+ if (
51
+ showSm === "sm:hidden" &&
52
+ showMd === "md:hidden" &&
53
+ showLg === "lg:hidden"
54
+ ) {
55
+ return null;
56
+ }
57
+
58
+ return (
59
+ <div
60
+ className={cn(
61
+ "pointer-events-none hidden",
62
+ showSm,
63
+ showMd,
64
+ showLg,
65
+ className
66
+ )}
67
+ key={`filler-${i}`}
68
+ {...props}
69
+ />
70
+ );
71
+ })}
72
+ </>
73
+ );
74
+ }
@@ -0,0 +1,5 @@
1
+ import { BlogsSection } from "../components/blogs";
2
+
3
+ export default function DemoPage() {
4
+ return <BlogsSection />;
5
+ }
@@ -0,0 +1,22 @@
1
+ import { cn } from "@/lib/utils"
2
+
3
+ function AspectRatio({
4
+ ratio,
5
+ className,
6
+ ...props
7
+ }: React.ComponentProps<"div"> & { ratio: number }) {
8
+ return (
9
+ <div
10
+ data-slot="aspect-ratio"
11
+ style={
12
+ {
13
+ "--ratio": ratio,
14
+ } as React.CSSProperties
15
+ }
16
+ className={cn("relative aspect-(--ratio)", className)}
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+
22
+ export { AspectRatio }
@@ -0,0 +1,224 @@
1
+ import { cn } from "@/lib/utils";
2
+ import { FullWidthDivider } from "./full-width-divider";
3
+ import { LazyImage } from "./lazy-image";
4
+
5
+ type BlogType = {
6
+ title: string;
7
+ href: string;
8
+ description: string;
9
+ author: string;
10
+ createdAt: string;
11
+ readTime: string;
12
+ image: string;
13
+ };
14
+
15
+ const wikimediaImage = (fileName: string, width = 900) =>
16
+ `https://commons.wikimedia.org/wiki/Special:Redirect/file/${encodeURIComponent(
17
+ fileName
18
+ )}?width=${width}`;
19
+
20
+ const blogs: BlogType[] = [
21
+ {
22
+ title: "Why LoveUI Ships Components as Source",
23
+ href: "#",
24
+ description:
25
+ "How copied source gives product teams the confidence to inspect, change, and own their interface layer.",
26
+ image: wikimediaImage(
27
+ "University Hall, The Ohio State University (Columbus, Ohio).jpg"
28
+ ),
29
+ createdAt: "2026-05-20",
30
+ author: "Connor Love",
31
+ readTime: "7 min read",
32
+ },
33
+ {
34
+ title: "Designing Blocks That Feel Ready",
35
+ href: "#",
36
+ description:
37
+ "The decisions behind LoveUI sections that look polished on day one and stay easy to reshape on day two.",
38
+ image: wikimediaImage("View of Ohio Stadium.jpg"),
39
+ createdAt: "2026-05-14",
40
+ author: "Connor Love",
41
+ readTime: "5 min read",
42
+ },
43
+ {
44
+ title: "Building Better Interfaces with AI Agents",
45
+ href: "#",
46
+ description:
47
+ "Why LoveUI includes agent skills with concrete design rules, implementation steps, and quality checks.",
48
+ image: wikimediaImage(
49
+ "Mirror Lake at The Ohio State University - DPLA - f1276865f9c52083353de2c6b0068290.jpg"
50
+ ),
51
+ createdAt: "2026-04-29",
52
+ author: "Connor Love",
53
+ readTime: "6 min read",
54
+ },
55
+ {
56
+ title: "Composing Product Pages from Primitives",
57
+ href: "#",
58
+ description:
59
+ "How buttons, accordions, empty states, and layout helpers become complete LoveUI product sections.",
60
+ image: wikimediaImage(
61
+ "Thompson Library at The Ohio State University - DPLA - d4a313f4ce0cca7538bf0ea6508722ae.jpg"
62
+ ),
63
+ createdAt: "2026-04-12",
64
+ author: "Connor Love",
65
+ readTime: "8 min read",
66
+ },
67
+ {
68
+ title: "Keeping Design Systems Close to the App",
69
+ href: "#",
70
+ description:
71
+ "LoveUI keeps components in your repo so teams can review, version, and adapt them with the rest of the product.",
72
+ image: wikimediaImage("Ohio State University-Ohio Stadium-Rotunda.jpg"),
73
+ createdAt: "2026-03-23",
74
+ author: "Connor Love",
75
+ readTime: "4 min read",
76
+ },
77
+ {
78
+ title: "What Makes a Registry Example Useful",
79
+ href: "#",
80
+ description:
81
+ "Examples should teach real composition patterns, not just show isolated visuals. That idea shapes LoveUI docs.",
82
+ image: wikimediaImage(
83
+ "The Spring and Mirror Lake at Ohio State University - DPLA - 238a754a6579c3fd1788745a85caaf4c (page 1).jpg"
84
+ ),
85
+ createdAt: "2026-03-05",
86
+ author: "Connor Love",
87
+ readTime: "9 min read",
88
+ },
89
+ {
90
+ title: "From Component to Full Section",
91
+ href: "#",
92
+ description:
93
+ "The practical path from reusable primitives to CTA, FAQ, pricing, testimonial, and 404 blocks.",
94
+ image: wikimediaImage(
95
+ "Ohio Stadium at The Ohio State University - DPLA - fa20937cd54b73c1c34c09ac5d4d3e6d.jpg"
96
+ ),
97
+ createdAt: "2026-02-18",
98
+ author: "Connor Love",
99
+ readTime: "10 min read",
100
+ },
101
+ {
102
+ title: "The Case for Owning Your UI",
103
+ href: "#",
104
+ description:
105
+ "When product details matter, editable source makes styling, behavior, and structure easier to reason about.",
106
+ image: wikimediaImage(
107
+ "Ohio State University - Thompson Library from First Floor.jpg"
108
+ ),
109
+ createdAt: "2026-02-02",
110
+ author: "Connor Love",
111
+ readTime: "6 min read",
112
+ },
113
+ {
114
+ title: "Why Blocks Need Real Constraints",
115
+ href: "#",
116
+ description:
117
+ "LoveUI blocks are built around realistic copy, responsive behavior, and controls that work in product contexts.",
118
+ image: wikimediaImage("OSU Ohio Stadium.JPG"),
119
+ createdAt: "2026-01-22",
120
+ author: "Connor Love",
121
+ readTime: "7 min read",
122
+ },
123
+ {
124
+ title: "Making Components Easier to Customize",
125
+ href: "#",
126
+ description:
127
+ "A look at naming, file structure, and examples that help LoveUI components stay understandable after install.",
128
+ image: wikimediaImage("Ohio Stadium (37516165921).jpg", 1200),
129
+ createdAt: "2026-01-09",
130
+ author: "Connor Love",
131
+ readTime: "5 min read",
132
+ },
133
+ {
134
+ title: "The LoveUI Approach to Product Polish",
135
+ href: "#",
136
+ description:
137
+ "Polish comes from spacing, states, hierarchy, and composition. LoveUI tries to make those choices reusable.",
138
+ image: wikimediaImage(
139
+ "Ohio State University campus - DPLA - 74170bd1ea1086f74720703e80e0dc4c.jpg"
140
+ ),
141
+ createdAt: "2025-12-18",
142
+ author: "Connor Love",
143
+ readTime: "8 min read",
144
+ },
145
+ {
146
+ title: "A Source-First UI Roadmap",
147
+ href: "#",
148
+ description:
149
+ "What is next for LoveUI: more blocks, stronger examples, and better guidance for teams building with AI.",
150
+ image: wikimediaImage(
151
+ "Ohio State University campus aerial view looking west - DPLA - cb1a04591333ede0f6ac506328c1c460.jpg"
152
+ ),
153
+ createdAt: "2025-12-02",
154
+ author: "Connor Love",
155
+ readTime: "6 min read",
156
+ },
157
+ ];
158
+
159
+ export function BlogsSection() {
160
+ return (
161
+ <div className="mx-auto w-full max-w-5xl grow">
162
+ <div className="space-y-1 px-4 py-8 md:px-6">
163
+ <h1 className="font-semibold text-2xl tracking-wide md:text-4xl">
164
+ LoveUI Field Notes
165
+ </h1>
166
+ <p className="text-muted-foreground text-sm md:text-base">
167
+ Writing from Connor Love on source-first UI, practical blocks, and
168
+ building better product surfaces.
169
+ </p>
170
+ </div>
171
+ <FullWidthDivider contained={true} />
172
+ <div className="z-10 grid p-4 md:grid-cols-2 lg:grid-cols-3">
173
+ {blogs.map((blog) => (
174
+ <BlogCard {...blog} key={blog.title} />
175
+ ))}
176
+ </div>
177
+ </div>
178
+ );
179
+ }
180
+
181
+ function BlogCard({
182
+ title,
183
+ description,
184
+ createdAt,
185
+ readTime,
186
+ image,
187
+ author,
188
+ className,
189
+ ...props
190
+ }: React.ComponentProps<"a"> & BlogType) {
191
+ return (
192
+ <a
193
+ className={cn(
194
+ "group cn-rounded flex flex-col gap-2 p-3 hover:bg-muted/50 active:bg-muted",
195
+ className
196
+ )}
197
+ key={title}
198
+ {...props}
199
+ >
200
+ <LazyImage
201
+ alt={title}
202
+ className="transition-all duration-500 group-hover:scale-105"
203
+ containerClassName="cn-rounded shadow-md outline outline-offset-3 outline-border/50"
204
+ fallback="https://placehold.co/640x360?text=fallback-image"
205
+ inView={true}
206
+ ratio={16 / 9}
207
+ src={image}
208
+ />
209
+ <div className="space-y-2 px-2 pb-2">
210
+ <div className="flex items-center gap-2 text-[11px] text-muted-foreground group-hover:text-foreground sm:text-xs">
211
+ <p>by {author}</p>
212
+ <div className="size-1 rounded-full bg-muted-foreground" />
213
+ <p>{createdAt}</p>
214
+ <div className="size-1 rounded-full bg-muted-foreground" />
215
+ <p>{readTime}</p>
216
+ </div>
217
+ <h2 className="line-clamp-2 font-semibold text-lg">{title}</h2>
218
+ <p className="line-clamp-3 text-muted-foreground text-sm group-active:text-foreground">
219
+ {description}
220
+ </p>
221
+ </div>
222
+ </a>
223
+ );
224
+ }
@@ -0,0 +1,33 @@
1
+ import { cn } from "@/lib/utils";
2
+
3
+ type FullWidthDividerProps = React.ComponentProps<"div"> & {
4
+ contained?: boolean;
5
+ position?: "top" | "bottom";
6
+ };
7
+
8
+ export function FullWidthDivider({
9
+ className,
10
+ contained = false,
11
+ position,
12
+ ...props
13
+ }: FullWidthDividerProps) {
14
+ return (
15
+ <div
16
+ aria-hidden="true"
17
+ className={cn(
18
+ "pointer-events-none absolute h-px bg-border",
19
+ // full-bleed (default)
20
+ "data-[contained=false]:left-1/2 data-[contained=false]:w-screen data-[contained=false]:-translate-x-1/2",
21
+ // contained
22
+ "data-[contained=true]:inset-x-0 data-[contained=true]:w-full",
23
+ // position
24
+ position &&
25
+ "data-[position=top]:-top-px data-[position=bottom]:-bottom-px",
26
+ className
27
+ )}
28
+ data-contained={contained}
29
+ data-position={position}
30
+ {...props}
31
+ />
32
+ );
33
+ }
@@ -0,0 +1,94 @@
1
+ "use client";
2
+
3
+ import { cn } from "@/lib/utils";
4
+ import { useInView } from "motion/react";
5
+ import React from "react";
6
+ import { AspectRatio } from "./aspect-ratio";
7
+
8
+ type LazyImageProps = {
9
+ alt: string;
10
+ src: string;
11
+ className?: string;
12
+ containerClassName?: string;
13
+ /** URL of the fallback image. default: undefined */
14
+ fallback?: string;
15
+ /** The ratio of the image. */
16
+ ratio: number;
17
+ /** Whether the image should only load when it is in view. default: false */
18
+ inView?: boolean;
19
+ };
20
+
21
+ export function LazyImage({
22
+ alt,
23
+ src,
24
+ ratio,
25
+ fallback,
26
+ inView = false,
27
+ className,
28
+ containerClassName,
29
+ }: LazyImageProps) {
30
+ const ref = React.useRef<HTMLDivElement | null>(null);
31
+ const imgRef = React.useRef<HTMLImageElement | null>(null);
32
+ const isInView = useInView(ref, { once: true });
33
+
34
+ const [imgSrc, setImgSrc] = React.useState<string | undefined>(
35
+ inView ? undefined : src
36
+ );
37
+ const [isLoading, setIsLoading] = React.useState(true);
38
+
39
+ const handleError = () => {
40
+ if (fallback) {
41
+ setImgSrc(fallback);
42
+ }
43
+ setIsLoading(false);
44
+ };
45
+
46
+ const handleLoad = React.useCallback(() => {
47
+ setIsLoading(false);
48
+ }, []);
49
+
50
+ // Load image only when inView
51
+ React.useEffect(() => {
52
+ if (inView && isInView && !imgSrc) {
53
+ setImgSrc(src);
54
+ }
55
+ }, [inView, isInView, src, imgSrc]);
56
+
57
+ // Handle cached images instantly
58
+ React.useEffect(() => {
59
+ if (imgRef.current?.complete) {
60
+ handleLoad();
61
+ }
62
+ }, [handleLoad]);
63
+
64
+ return (
65
+ <AspectRatio
66
+ className={cn(
67
+ "relative size-full overflow-hidden border bg-accent/30",
68
+ containerClassName
69
+ )}
70
+ ratio={ratio}
71
+ ref={ref}
72
+ >
73
+ {imgSrc && (
74
+ // biome-ignore lint/correctness/useImageSize: dynamic image size
75
+ <img
76
+ alt={alt}
77
+ className={cn(
78
+ "size-full object-cover transition-opacity duration-500",
79
+ isLoading ? "opacity-0" : "opacity-100",
80
+ className
81
+ )}
82
+ decoding="async"
83
+ fetchPriority={inView ? "high" : "low"}
84
+ loading="lazy"
85
+ onError={handleError}
86
+ onLoad={handleLoad}
87
+ ref={imgRef}
88
+ role="presentation" // Changed from "img" to "presentation" since it's decorative
89
+ src={imgSrc}
90
+ />
91
+ )}
92
+ </AspectRatio>
93
+ );
94
+ }
@@ -0,0 +1,9 @@
1
+ import { Contact } from "../components/contact";
2
+
3
+ export default function Page() {
4
+ return (
5
+ <div className="min-h-screen w-full place-content-center overflow-hidden px-4">
6
+ <Contact />
7
+ </div>
8
+ );
9
+ }
@@ -0,0 +1,65 @@
1
+ import { cn } from "@/lib/utils";
2
+ import { FullWidthDivider } from "./full-width-divider";
3
+ import { Phone, Mail, MapPin } from "lucide-react";
4
+
5
+ const data = [
6
+ {
7
+ title: "Ask about LoveUI",
8
+ value: "+1 (555) 014-2026",
9
+ icon: (
10
+ <Phone
11
+ />
12
+ ),
13
+ },
14
+ {
15
+ title: "Email the team",
16
+ value: "hello@loveui.dev",
17
+ icon: (
18
+ <Mail
19
+ />
20
+ ),
21
+ },
22
+ {
23
+ title: "Product studio",
24
+ value: "Built by Connor Love",
25
+ icon: (
26
+ <MapPin
27
+ />
28
+ ),
29
+ },
30
+ ];
31
+
32
+ export function Contact() {
33
+ return (
34
+ <div className="mx-auto max-w-4xl">
35
+ <h2 className="mb-6 font-medium text-lg md:text-2xl">
36
+ Need help building with LoveUI?
37
+ </h2>
38
+ <div className="relative">
39
+ <FullWidthDivider position="top" />
40
+ <div className="grid gap-px overflow-hidden bg-border px-px md:grid-cols-3">
41
+ {data.map((item) => (
42
+ <div
43
+ className="flex items-center gap-3 bg-background p-2 shadow-xs"
44
+ key={item.title}
45
+ >
46
+ <div
47
+ className={cn(
48
+ "flex size-12 shrink-0 items-center justify-center rounded-lg bg-muted/50",
49
+ "[&_svg]:size-4 [&_svg]:text-muted-foreground"
50
+ )}
51
+ >
52
+ {item.icon}
53
+ </div>
54
+ <div className={cn("flex flex-col gap-y-0.5")}>
55
+ <h2 className="text-sm">{item.title}</h2>
56
+ <p className="text-muted-foreground text-xs">{item.value}</p>
57
+ </div>
58
+ </div>
59
+ ))}
60
+ </div>
61
+ <FullWidthDivider position="bottom" />
62
+ </div>
63
+ </div>
64
+ );
65
+ }
@@ -0,0 +1,33 @@
1
+ import { cn } from "@/lib/utils";
2
+
3
+ type FullWidthDividerProps = React.ComponentProps<"div"> & {
4
+ contained?: boolean;
5
+ position?: "top" | "bottom";
6
+ };
7
+
8
+ export function FullWidthDivider({
9
+ className,
10
+ contained = false,
11
+ position,
12
+ ...props
13
+ }: FullWidthDividerProps) {
14
+ return (
15
+ <div
16
+ aria-hidden="true"
17
+ className={cn(
18
+ "pointer-events-none absolute h-px bg-border",
19
+ // full-bleed (default)
20
+ "data-[contained=false]:left-1/2 data-[contained=false]:w-screen data-[contained=false]:-translate-x-1/2",
21
+ // contained
22
+ "data-[contained=true]:inset-x-0 data-[contained=true]:w-full",
23
+ // position
24
+ position &&
25
+ "data-[position=top]:-top-px data-[position=bottom]:-bottom-px",
26
+ className
27
+ )}
28
+ data-contained={contained}
29
+ data-position={position}
30
+ {...props}
31
+ />
32
+ );
33
+ }
@@ -0,0 +1,9 @@
1
+ import { Contact } from "../components/contact";
2
+
3
+ export default function Page() {
4
+ return (
5
+ <div className="min-h-screen w-full place-content-center overflow-hidden px-4">
6
+ <Contact />
7
+ </div>
8
+ );
9
+ }
@@ -0,0 +1,74 @@
1
+ import { cn } from "@/lib/utils";
2
+ import { XIcon } from "./icons/x-icon";
3
+ import { Button } from "@/registry/default/ui/button";
4
+ import { Mail, Users } from "lucide-react";
5
+
6
+ const APP_EMAIL = "hello@loveui.dev";
7
+
8
+ const data = [
9
+ {
10
+ title: "Email LoveUI",
11
+ description: "Ask about components, blocks, registry setup, or implementation details.",
12
+ icon: (
13
+ <Mail
14
+ />
15
+ ),
16
+ href: `mailto:${APP_EMAIL}`,
17
+ label: APP_EMAIL,
18
+ },
19
+ {
20
+ title: "Send a DM",
21
+ description: "Reach out on X with LoveUI questions, ideas, or bug reports.",
22
+ icon: <XIcon />,
23
+ href: "#",
24
+ label: "@connorlove",
25
+ },
26
+ {
27
+ title: "Join the community",
28
+ description: "Connect with builders using LoveUI in real product interfaces.",
29
+ icon: (
30
+ <Users
31
+ />
32
+ ),
33
+ href: "#",
34
+ label: "Join Discord",
35
+ },
36
+ ];
37
+
38
+ export function Contact() {
39
+ return (
40
+ <div className="mx-auto max-w-4xl">
41
+ <div className="mb-12 flex max-w-md flex-col justify-center gap-2">
42
+ <h1 className="font-bold text-2xl md:text-3xl">Contact Us</h1>
43
+ <p className="text-base text-muted-foreground">
44
+ Get help with LoveUI components, blocks, registry installs, and
45
+ source-first UI workflows.
46
+ </p>
47
+ </div>
48
+ <div className="grid gap-0.5 overflow-hidden rounded-lg bg-muted p-0.5 md:grid-cols-3 dark:bg-muted/50">
49
+ {data.map((item) => (
50
+ <div
51
+ className="flex flex-col gap-3 rounded-lg bg-background px-6 py-6 shadow-xs"
52
+ key={item.title}
53
+ >
54
+ <div
55
+ className={cn(
56
+ "flex items-center gap-x-2",
57
+ "[&_svg]:size-4 [&_svg]:text-muted-foreground"
58
+ )}
59
+ >
60
+ {item.icon}
61
+ <h2 className="text-sm">{item.title}</h2>
62
+ </div>
63
+ <p className="text-muted-foreground text-sm">{item.description}</p>
64
+ <div className="mt-1 flex items-center gap-x-2">
65
+ <Button asChild variant="link">
66
+ <a href={item.href}>{item.label}</a>
67
+ </Button>
68
+ </div>
69
+ </div>
70
+ ))}
71
+ </div>
72
+ </div>
73
+ );
74
+ }
@@ -0,0 +1,33 @@
1
+ import { cn } from "@/lib/utils";
2
+
3
+ type FullWidthDividerProps = React.ComponentProps<"div"> & {
4
+ contained?: boolean;
5
+ position?: "top" | "bottom";
6
+ };
7
+
8
+ export function FullWidthDivider({
9
+ className,
10
+ contained = false,
11
+ position,
12
+ ...props
13
+ }: FullWidthDividerProps) {
14
+ return (
15
+ <div
16
+ aria-hidden="true"
17
+ className={cn(
18
+ "pointer-events-none absolute h-px bg-border",
19
+ // full-bleed (default)
20
+ "data-[contained=false]:left-1/2 data-[contained=false]:w-screen data-[contained=false]:-translate-x-1/2",
21
+ // contained
22
+ "data-[contained=true]:inset-x-0 data-[contained=true]:w-full",
23
+ // position
24
+ position &&
25
+ "data-[position=top]:-top-px data-[position=bottom]:-bottom-px",
26
+ className
27
+ )}
28
+ data-contained={contained}
29
+ data-position={position}
30
+ {...props}
31
+ />
32
+ );
33
+ }
@@ -0,0 +1,12 @@
1
+ export function XIcon(props: React.ComponentProps<"svg">) {
2
+ return (
3
+ <svg
4
+ fill="currentColor"
5
+ viewBox="0 0 24 24"
6
+ xmlns="http://www.w3.org/2000/svg"
7
+ {...props}
8
+ >
9
+ <path d="m18.9,1.153h3.682l-8.042,9.189,9.46,12.506h-7.405l-5.804-7.583-6.634,7.583H.469l8.6-9.831L0,1.153h7.593l5.241,6.931,6.065-6.931Zm-1.293,19.494h2.039L6.482,3.239h-2.19l13.314,17.408Z" />
10
+ </svg>
11
+ );
12
+ }