fulldev-ui 0.9.2 → 0.10.0

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 (255) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +100 -16
  3. package/package.json +3 -2
  4. package/public/r/accordion.json +3 -5
  5. package/public/r/alert-dialog.json +1 -1
  6. package/public/r/alert.json +0 -5
  7. package/public/r/article-1.json +1 -1
  8. package/public/r/article-2.json +1 -1
  9. package/public/r/articles-1.json +1 -1
  10. package/public/r/articles-2.json +1 -1
  11. package/public/r/articles-3.json +1 -1
  12. package/public/r/avatar.json +1 -16
  13. package/public/r/badge.json +1 -1
  14. package/public/r/banner-1.json +5 -3
  15. package/public/r/banner-2.json +1 -1
  16. package/public/r/banner.json +5 -3
  17. package/public/r/blocks-1.json +6 -3
  18. package/public/r/breadcrumb.json +4 -4
  19. package/public/r/button.json +1 -1
  20. package/public/r/card.json +4 -4
  21. package/public/r/carousel.json +5 -5
  22. package/public/r/checkbox.json +1 -1
  23. package/public/r/combobox.json +8 -8
  24. package/public/r/command.json +6 -6
  25. package/public/r/components.json +1 -6
  26. package/public/r/contact-1.json +4 -2
  27. package/public/r/contact-2.json +4 -2
  28. package/public/r/contact-3.json +11 -3
  29. package/public/r/content-1.json +11 -3
  30. package/public/r/content-2.json +11 -3
  31. package/public/r/content-3.json +7 -1
  32. package/public/r/content-4.json +11 -3
  33. package/public/r/cta-2.json +7 -1
  34. package/public/r/cta-4.json +7 -1
  35. package/public/r/cta-5.json +11 -3
  36. package/public/r/cta-6.json +7 -1
  37. package/public/r/dialog.json +6 -6
  38. package/public/r/doc-1.json +6 -3
  39. package/public/r/dropdown-menu.json +9 -11
  40. package/public/r/features-1.json +5 -3
  41. package/public/r/features-2.json +5 -3
  42. package/public/r/features-4.json +5 -3
  43. package/public/r/features-5.json +4 -2
  44. package/public/r/field.json +2 -7
  45. package/public/r/footer-1.json +5 -3
  46. package/public/r/footer-2.json +5 -3
  47. package/public/r/footer-3.json +5 -3
  48. package/public/r/header-1.json +1 -1
  49. package/public/r/header.json +3 -0
  50. package/public/r/hero-13.json +1 -1
  51. package/public/r/hero-4.json +5 -3
  52. package/public/r/hero-5.json +5 -3
  53. package/public/r/hero-6.json +5 -3
  54. package/public/r/hero-7.json +5 -3
  55. package/public/r/hero-8.json +1 -1
  56. package/public/r/hero-9.json +5 -3
  57. package/public/r/hover-card.json +2 -2
  58. package/public/r/icon.json +1 -1
  59. package/public/r/input-group.json +1 -1
  60. package/public/r/item.json +2 -20
  61. package/public/r/kbd.json +2 -2
  62. package/public/r/layout.json +4 -7
  63. package/public/r/links-2.json +5 -3
  64. package/public/r/marquee.json +1 -1
  65. package/public/r/native-select.json +3 -3
  66. package/public/r/navigation-menu.json +6 -8
  67. package/public/r/page.json +17 -0
  68. package/public/r/popover.json +1 -1
  69. package/public/r/pricing-1.json +5 -3
  70. package/public/r/pricing-2.json +5 -3
  71. package/public/r/pricing-3.json +5 -3
  72. package/public/r/products-1.json +1 -1
  73. package/public/r/products-4.json +1 -1
  74. package/public/r/products-5.json +1 -1
  75. package/public/r/radio-group.json +1 -1
  76. package/public/r/rating.json +3 -3
  77. package/public/r/registry.json +186 -440
  78. package/public/r/reviews-1.json +5 -3
  79. package/public/r/reviews-2.json +5 -3
  80. package/public/r/reviews-3.json +5 -3
  81. package/public/r/reviews-4.json +1 -1
  82. package/public/r/reviews-5.json +5 -3
  83. package/public/r/select.json +6 -8
  84. package/public/r/services-1.json +11 -3
  85. package/public/r/services-2.json +7 -1
  86. package/public/r/services-3.json +7 -1
  87. package/public/r/services-4.json +11 -3
  88. package/public/r/services-5.json +5 -3
  89. package/public/r/services-6.json +11 -3
  90. package/public/r/services-7.json +11 -3
  91. package/public/r/sheet.json +6 -3
  92. package/public/r/sidebar-1.json +4 -2
  93. package/public/r/sidebar.json +10 -7
  94. package/public/r/slider.json +1 -1
  95. package/public/r/spinner.json +3 -3
  96. package/public/r/switch.json +1 -1
  97. package/public/r/theme-toggle.json +5 -3
  98. package/public/r/tooltip.json +1 -1
  99. package/public/r/typography.json +16 -16
  100. package/public/r/video.json +1 -1
  101. package/registry.json +186 -440
  102. package/src/components/blocks/article-1.astro +1 -1
  103. package/src/components/blocks/article-2.astro +1 -1
  104. package/src/components/blocks/articles-1.astro +1 -1
  105. package/src/components/blocks/articles-2.astro +2 -2
  106. package/src/components/blocks/articles-3.astro +1 -1
  107. package/src/components/blocks/banner-1.astro +3 -5
  108. package/src/components/blocks/banner-2.astro +1 -1
  109. package/src/components/blocks/blocks-1.astro +25 -37
  110. package/src/components/blocks/contact-3.astro +4 -3
  111. package/src/components/blocks/content-1.astro +5 -4
  112. package/src/components/blocks/content-2.astro +6 -5
  113. package/src/components/blocks/content-3.astro +9 -2
  114. package/src/components/blocks/content-4.astro +6 -5
  115. package/src/components/blocks/cta-2.astro +4 -3
  116. package/src/components/blocks/cta-4.astro +3 -2
  117. package/src/components/blocks/cta-5.astro +6 -5
  118. package/src/components/blocks/cta-6.astro +4 -3
  119. package/src/components/blocks/doc-1.astro +28 -56
  120. package/src/components/blocks/features-1.astro +3 -1
  121. package/src/components/blocks/features-2.astro +4 -2
  122. package/src/components/blocks/features-4.astro +3 -1
  123. package/src/components/blocks/footer-1.astro +26 -12
  124. package/src/components/blocks/footer-2.astro +24 -12
  125. package/src/components/blocks/footer-3.astro +26 -12
  126. package/src/components/blocks/header-1.astro +2 -3
  127. package/src/components/blocks/hero-13.astro +1 -1
  128. package/src/components/blocks/hero-4.astro +2 -2
  129. package/src/components/blocks/hero-5.astro +3 -3
  130. package/src/components/blocks/hero-6.astro +3 -3
  131. package/src/components/blocks/hero-7.astro +3 -3
  132. package/src/components/blocks/hero-8.astro +1 -1
  133. package/src/components/blocks/hero-9.astro +2 -2
  134. package/src/components/blocks/links-2.astro +3 -5
  135. package/src/components/blocks/pricing-1.astro +3 -2
  136. package/src/components/blocks/pricing-2.astro +3 -2
  137. package/src/components/blocks/pricing-3.astro +3 -2
  138. package/src/components/blocks/products-1.astro +1 -1
  139. package/src/components/blocks/products-4.astro +2 -2
  140. package/src/components/blocks/products-5.astro +4 -4
  141. package/src/components/blocks/reviews-1.astro +2 -2
  142. package/src/components/blocks/reviews-2.astro +2 -2
  143. package/src/components/blocks/reviews-3.astro +2 -2
  144. package/src/components/blocks/reviews-4.astro +4 -4
  145. package/src/components/blocks/reviews-5.astro +16 -25
  146. package/src/components/blocks/services-1.astro +7 -9
  147. package/src/components/blocks/services-2.astro +5 -4
  148. package/src/components/blocks/services-3.astro +6 -5
  149. package/src/components/blocks/services-4.astro +7 -9
  150. package/src/components/blocks/services-5.astro +3 -4
  151. package/src/components/blocks/services-6.astro +7 -6
  152. package/src/components/blocks/services-7.astro +7 -9
  153. package/src/components/blocks/sidebar-1.astro +42 -97
  154. package/src/components/ui/accordion/accordion-trigger.astro +7 -8
  155. package/src/components/ui/alert-dialog/alert-dialog.astro +23 -3
  156. package/src/components/ui/avatar/avatar.astro +1 -1
  157. package/src/components/ui/badge/badge.astro +1 -0
  158. package/src/components/ui/banner/banner-container.astro +3 -3
  159. package/src/components/ui/breadcrumb/breadcrumb-ellipsis.astro +2 -2
  160. package/src/components/ui/breadcrumb/breadcrumb-separator.astro +2 -2
  161. package/src/components/ui/button/button-variants.ts +8 -7
  162. package/src/components/ui/card/card-content.astro +1 -1
  163. package/src/components/ui/card/card-footer.astro +1 -1
  164. package/src/components/ui/card/card-header.astro +1 -1
  165. package/src/components/ui/card/card.astro +1 -1
  166. package/src/components/ui/carousel/carousel-next.astro +3 -3
  167. package/src/components/ui/carousel/carousel-previous.astro +3 -3
  168. package/src/components/ui/checkbox/checkbox.astro +1 -1
  169. package/src/components/ui/combobox/combobox-input.astro +2 -2
  170. package/src/components/ui/combobox/combobox-item.astro +4 -4
  171. package/src/components/ui/combobox/combobox-list.astro +1 -1
  172. package/src/components/ui/combobox/combobox-trigger.astro +2 -5
  173. package/src/components/ui/combobox/combobox.astro +3 -0
  174. package/src/components/ui/command/command-dialog.astro +10 -10
  175. package/src/components/ui/command/command-input.astro +2 -2
  176. package/src/components/ui/command/command-item.astro +4 -5
  177. package/src/components/ui/dialog/dialog-close.astro +2 -2
  178. package/src/components/ui/dialog/dialog-content.astro +2 -2
  179. package/src/components/ui/dialog/dialog.astro +26 -3
  180. package/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.astro +4 -5
  181. package/src/components/ui/dropdown-menu/dropdown-menu-content.astro +4 -4
  182. package/src/components/ui/dropdown-menu/dropdown-menu-item.astro +1 -1
  183. package/src/components/ui/dropdown-menu/dropdown-menu-label.astro +1 -1
  184. package/src/components/ui/dropdown-menu/dropdown-menu-radio-item.astro +4 -5
  185. package/src/components/ui/dropdown-menu/dropdown-menu-shortcut.astro +1 -1
  186. package/src/components/ui/dropdown-menu/dropdown-menu.astro +3 -0
  187. package/src/components/ui/field/field-label.astro +1 -1
  188. package/src/components/ui/field/field-title.astro +1 -1
  189. package/src/components/ui/hover-card/hover-card-content.astro +4 -4
  190. package/src/components/ui/hover-card/hover-card.astro +2 -2
  191. package/src/components/ui/icon/icon.astro +7 -10
  192. package/src/components/ui/input-group/input-group-button.astro +0 -1
  193. package/src/components/ui/item/item-media.astro +1 -1
  194. package/src/components/ui/item/item.astro +0 -18
  195. package/src/components/ui/kbd/kbd-group.astro +1 -1
  196. package/src/components/ui/kbd/kbd.astro +1 -1
  197. package/src/components/ui/layout/layout-body.astro +1 -1
  198. package/src/components/ui/layout/layout-head.astro +3 -2
  199. package/src/components/ui/layout/layout-main.astro +1 -1
  200. package/src/components/ui/layout/layout.astro +4 -0
  201. package/src/components/ui/marquee/marquee.astro +49 -68
  202. package/src/components/ui/native-select/native-select.astro +2 -3
  203. package/src/components/ui/navigation-menu/navigation-menu-content.astro +1 -1
  204. package/src/components/ui/navigation-menu/navigation-menu-list.astro +1 -1
  205. package/src/components/ui/navigation-menu/navigation-menu-trigger.astro +2 -3
  206. package/src/components/ui/navigation-menu/navigation-menu.astro +1 -1
  207. package/src/components/ui/popover/popover-content.astro +1 -1
  208. package/src/components/ui/radio-group/radio-group-item.astro +3 -3
  209. package/src/components/ui/rating/rating.astro +3 -3
  210. package/src/components/ui/select/select-content.astro +1 -1
  211. package/src/components/ui/select/select-item.astro +4 -4
  212. package/src/components/ui/select/select-trigger.astro +3 -6
  213. package/src/components/ui/select/select.astro +6 -0
  214. package/src/components/ui/sheet/sheet-content.astro +4 -5
  215. package/src/components/ui/sidebar/sidebar-menu-button.astro +13 -10
  216. package/src/components/ui/sidebar/sidebar-menu-sub-button.astro +10 -8
  217. package/src/components/ui/sidebar/sidebar-provider.astro +1 -13
  218. package/src/components/ui/sidebar/sidebar-rail.astro +1 -1
  219. package/src/components/ui/sidebar/sidebar-trigger.astro +3 -3
  220. package/src/components/ui/sidebar/sidebar.astro +1 -1
  221. package/src/components/ui/slider/slider.astro +6 -4
  222. package/src/components/ui/spinner/spinner.astro +4 -4
  223. package/src/components/ui/switch/switch.astro +1 -1
  224. package/src/components/ui/theme-toggle/theme-toggle.astro +4 -3
  225. package/src/components/ui/tooltip/tooltip-content.astro +1 -1
  226. package/src/components/ui/typography/typography-a.astro +19 -4
  227. package/src/components/ui/typography/typography-blockquote.astro +26 -6
  228. package/src/components/ui/typography/typography-h1.astro +7 -9
  229. package/src/components/ui/typography/typography-h2.astro +12 -9
  230. package/src/components/ui/typography/typography-h3.astro +7 -9
  231. package/src/components/ui/typography/typography-h4.astro +7 -9
  232. package/src/components/ui/typography/typography-inline-code.astro +28 -12
  233. package/src/components/ui/typography/typography-lead.astro +29 -4
  234. package/src/components/ui/typography/typography-list-item.astro +25 -9
  235. package/src/components/ui/typography/typography-list.astro +29 -11
  236. package/src/components/ui/typography/typography-p.astro +26 -6
  237. package/src/components/ui/typography/typography-table-cell.astro +32 -13
  238. package/src/components/ui/typography/typography-table-head.astro +27 -13
  239. package/src/components/ui/typography/typography-table-row.astro +23 -13
  240. package/src/components/ui/typography/typography-table.astro +29 -20
  241. package/src/components/ui/typography/typography.astro +172 -46
  242. package/src/components/ui/video/video.astro +11 -27
  243. package/src/styles/global.css +2 -15
  244. package/public/r/beta-repo-setup.json +0 -131
  245. package/public/r/blocks.json +0 -91
  246. package/public/r/content-5.json +0 -17
  247. package/public/r/content-6.json +0 -17
  248. package/public/r/init.json +0 -23
  249. package/public/r/tile.json +0 -27
  250. package/src/components/blocks/content-5.astro +0 -115
  251. package/src/components/blocks/content-6.astro +0 -91
  252. package/src/components/ui/tile/index.ts +0 -3
  253. package/src/components/ui/tile/tile-content.astro +0 -17
  254. package/src/components/ui/tile/tile-group.astro +0 -76
  255. package/src/components/ui/tile/tile.astro +0 -46
@@ -1,4 +1,6 @@
1
1
  ---
2
+ import { CheckIcon } from "@lucide/astro"
3
+
2
4
  import { cn } from "@/lib/utils"
3
5
  import { Badge } from "@/components/ui/badge"
4
6
  import { Button } from "@/components/ui/button"
@@ -10,7 +12,6 @@ import {
10
12
  CardHeader,
11
13
  CardTitle,
12
14
  } from "@/components/ui/card"
13
- import { Icon } from "@/components/ui/icon"
14
15
  import { Price, PriceUnit, PriceValue } from "@/components/ui/price"
15
16
  import { Section, SectionContainer } from "@/components/ui/section"
16
17
  import { Separator } from "@/components/ui/separator"
@@ -70,7 +71,7 @@ const { class: className, badge, title, description, offer } = Astro.props
70
71
  {
71
72
  offer.features.map((feature) => (
72
73
  <li class="flex items-center gap-3 text-sm">
73
- <Icon name="check" class="text-primary size-4 shrink-0" />
74
+ <CheckIcon class="text-primary size-4 shrink-0" />
74
75
  {feature}
75
76
  </li>
76
77
  ))
@@ -53,7 +53,7 @@ const { class: className, badge, title, products } = Astro.props
53
53
  <Image
54
54
  src={product.image.src}
55
55
  alt={product.image.alt}
56
- class="aspect-4/3 w-full object-cover"
56
+ class="aspect-[4/3] w-full object-cover"
57
57
  />
58
58
  <CardHeader>
59
59
  <CardTitle>{product.title}</CardTitle>
@@ -56,7 +56,7 @@ const { class: className, title, description, buttons, products } = Astro.props
56
56
  {description}
57
57
  </p>
58
58
  </div>
59
- <div class="flex shrink-0 flex-wrap gap-3">
59
+ <div class="flex flex-shrink-0 flex-wrap gap-3">
60
60
  {
61
61
  buttons.map(({ href, label, ...button }) => (
62
62
  <Button as="a" href={href} variant={button.variant}>
@@ -73,7 +73,7 @@ const { class: className, title, description, buttons, products } = Astro.props
73
73
  <Image
74
74
  src={product.image.src}
75
75
  alt={product.image.alt}
76
- class="aspect-4/3 w-full object-cover"
76
+ class="aspect-[4/3] w-full object-cover"
77
77
  />
78
78
  <CardHeader>
79
79
  <CardTitle>{product.title}</CardTitle>
@@ -64,19 +64,19 @@ const { class: className, badge, title, buttons, products } = Astro.props
64
64
  <div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
65
65
  {
66
66
  products.map((product) => (
67
- <Card class="group/product gap-0 overflow-hidden py-0">
67
+ <Card class="group/product gap-0 overflow-hidden">
68
68
  <div class="overflow-hidden">
69
69
  <Image
70
70
  src={product.image.src}
71
71
  alt={product.image.alt}
72
- class="aspect-3/4 w-full object-cover transition-transform duration-300 group-hover/product:scale-105"
72
+ class="aspect-[3/4] w-full object-cover transition-transform duration-300 group-hover/product:scale-105"
73
73
  />
74
74
  </div>
75
- <CardHeader class="pt-6">
75
+ <CardHeader>
76
76
  <CardTitle>{product.title}</CardTitle>
77
77
  <CardDescription>{product.description}</CardDescription>
78
78
  </CardHeader>
79
- <CardContent class="pt-4 pb-6">
79
+ <CardContent>
80
80
  <Price>
81
81
  <PriceValue
82
82
  price={product.price.value}
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  import type { ImageMetadata } from "astro"
3
+ import QuoteIcon from "@lucide/astro/icons/quote"
3
4
 
4
5
  import { cn } from "@/lib/utils"
5
6
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
6
7
  import { Card, CardContent } from "@/components/ui/card"
7
- import { Icon } from "@/components/ui/icon"
8
8
  import { Rating } from "@/components/ui/rating"
9
9
  import { Section, SectionContainer } from "@/components/ui/section"
10
10
 
@@ -50,7 +50,7 @@ const { class: className, title, description, reviews } = Astro.props
50
50
  <CardContent class="flex flex-col gap-4">
51
51
  <div class="flex items-center justify-between">
52
52
  <Rating value={item.rating} />
53
- <Icon name="quote" class="text-muted-foreground/20 size-6" />
53
+ <QuoteIcon class="text-muted-foreground/20 size-6" />
54
54
  </div>
55
55
  <p class="text-muted-foreground text-sm leading-relaxed">
56
56
  {item.quote}
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  import type { ImageMetadata } from "astro"
3
+ import QuoteIcon from "@lucide/astro/icons/quote"
3
4
 
4
5
  import { cn } from "@/lib/utils"
5
6
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
6
- import { Icon } from "@/components/ui/icon"
7
7
  import { Rating } from "@/components/ui/rating"
8
8
  import { Section, SectionContainer } from "@/components/ui/section"
9
9
  import { Separator } from "@/components/ui/separator"
@@ -35,7 +35,7 @@ const { class: className, featured, reviews } = Astro.props
35
35
  class="grid gap-10 lg:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)] lg:items-start"
36
36
  >
37
37
  <article class="flex flex-col gap-6 rounded-xl border p-8">
38
- <Icon name="quote" class="text-primary size-8" />
38
+ <QuoteIcon class="text-primary size-8" />
39
39
  <blockquote
40
40
  class="text-foreground text-2xl leading-snug font-semibold text-balance sm:text-3xl"
41
41
  >
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import type { ImageMetadata } from "astro"
3
+ import QuoteIcon from "@lucide/astro/icons/quote"
3
4
 
4
5
  import { cn } from "@/lib/utils"
5
6
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
@@ -10,7 +11,6 @@ import {
10
11
  CarouselNext,
11
12
  CarouselPrevious,
12
13
  } from "@/components/ui/carousel"
13
- import { Icon } from "@/components/ui/icon"
14
14
  import { Rating } from "@/components/ui/rating"
15
15
  import { Section, SectionContainer } from "@/components/ui/section"
16
16
 
@@ -55,7 +55,7 @@ const { class: className, title, description, reviews } = Astro.props
55
55
  reviews.map((item) => (
56
56
  <CarouselItem>
57
57
  <div class="flex flex-col items-center gap-6 px-4 py-8 text-center">
58
- <Icon name="quote" class="text-primary size-10" />
58
+ <QuoteIcon class="text-primary size-10" />
59
59
  <Rating value={item.rating} />
60
60
  <blockquote class="text-foreground text-xl leading-relaxed font-medium text-balance sm:text-2xl">
61
61
  {item.quote}
@@ -57,9 +57,9 @@ const bottomRow = reviews.slice(half)
57
57
  {
58
58
  topRow.map((item) => (
59
59
  <Card class="flex w-[22rem] shrink-0 self-stretch">
60
- <CardContent class="flex min-w-0 flex-1 flex-col gap-3">
60
+ <CardContent class="flex flex-1 flex-col gap-3">
61
61
  <Rating value={item.rating} />
62
- <p class="text-muted-foreground min-w-0 text-sm leading-relaxed">
62
+ <p class="text-muted-foreground text-sm leading-relaxed">
63
63
  {item.quote}
64
64
  </p>
65
65
  <div class="mt-auto flex items-center gap-3 pt-1">
@@ -87,9 +87,9 @@ const bottomRow = reviews.slice(half)
87
87
  {
88
88
  bottomRow.map((item) => (
89
89
  <Card class="flex w-[22rem] shrink-0 self-stretch">
90
- <CardContent class="flex min-w-0 flex-1 flex-col gap-3">
90
+ <CardContent class="flex flex-1 flex-col gap-3">
91
91
  <Rating value={item.rating} />
92
- <p class="text-muted-foreground min-w-0 text-sm leading-relaxed">
92
+ <p class="text-muted-foreground text-sm leading-relaxed">
93
93
  {item.quote}
94
94
  </p>
95
95
  <div class="mt-auto flex items-center gap-3 pt-1">
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  import type { ImageMetadata } from "astro"
3
+ import QuoteIcon from "@lucide/astro/icons/quote"
3
4
 
4
5
  import { cn } from "@/lib/utils"
5
6
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
6
- import { Icon } from "@/components/ui/icon"
7
7
  import { Marquee } from "@/components/ui/marquee"
8
8
  import { Rating } from "@/components/ui/rating"
9
9
  import { Section, SectionContainer } from "@/components/ui/section"
@@ -57,8 +57,8 @@ const row3 = reviews.slice(third * 2)
57
57
  >
58
58
  {
59
59
  row1.map((item) => (
60
- <div class="bg-muted/50 flex w-104 shrink-0 rounded-2xl p-6">
61
- <div class="flex min-w-0 flex-1 flex-col gap-4">
60
+ <div class="bg-muted/50 flex w-[26rem] shrink-0 rounded-2xl p-6">
61
+ <div class="flex flex-1 flex-col gap-4">
62
62
  <div class="flex items-center justify-between">
63
63
  <div class="flex items-center gap-3">
64
64
  <Avatar size="lg">
@@ -74,12 +74,9 @@ const row3 = reviews.slice(third * 2)
74
74
  </div>
75
75
  <Rating value={item.rating} />
76
76
  </div>
77
- <div class="flex min-w-0 gap-2.5">
78
- <Icon
79
- name="quote"
80
- class="text-primary/30 mt-0.5 size-5 shrink-0"
81
- />
82
- <p class="text-foreground/80 min-w-0 text-sm leading-relaxed">
77
+ <div class="flex gap-2.5">
78
+ <QuoteIcon class="text-primary/30 mt-0.5 size-5 shrink-0" />
79
+ <p class="text-foreground/80 text-sm leading-relaxed">
83
80
  {item.quote}
84
81
  </p>
85
82
  </div>
@@ -97,8 +94,8 @@ const row3 = reviews.slice(third * 2)
97
94
  >
98
95
  {
99
96
  row2.map((item) => (
100
- <div class="bg-muted/50 flex w-104 shrink-0 rounded-2xl p-6">
101
- <div class="flex min-w-0 flex-1 flex-col gap-4">
97
+ <div class="bg-muted/50 flex w-[26rem] shrink-0 rounded-2xl p-6">
98
+ <div class="flex flex-1 flex-col gap-4">
102
99
  <div class="flex items-center justify-between">
103
100
  <div class="flex items-center gap-3">
104
101
  <Avatar size="lg">
@@ -114,12 +111,9 @@ const row3 = reviews.slice(third * 2)
114
111
  </div>
115
112
  <Rating value={item.rating} />
116
113
  </div>
117
- <div class="flex min-w-0 gap-2.5">
118
- <Icon
119
- name="quote"
120
- class="text-primary/30 mt-0.5 size-5 shrink-0"
121
- />
122
- <p class="text-foreground/80 min-w-0 text-sm leading-relaxed">
114
+ <div class="flex gap-2.5">
115
+ <QuoteIcon class="text-primary/30 mt-0.5 size-5 shrink-0" />
116
+ <p class="text-foreground/80 text-sm leading-relaxed">
123
117
  {item.quote}
124
118
  </p>
125
119
  </div>
@@ -137,8 +131,8 @@ const row3 = reviews.slice(third * 2)
137
131
  >
138
132
  {
139
133
  row3.map((item) => (
140
- <div class="bg-muted/50 flex w-104 shrink-0 rounded-2xl p-6">
141
- <div class="flex min-w-0 flex-1 flex-col gap-4">
134
+ <div class="bg-muted/50 flex w-[26rem] shrink-0 rounded-2xl p-6">
135
+ <div class="flex flex-1 flex-col gap-4">
142
136
  <div class="flex items-center justify-between">
143
137
  <div class="flex items-center gap-3">
144
138
  <Avatar size="lg">
@@ -154,12 +148,9 @@ const row3 = reviews.slice(third * 2)
154
148
  </div>
155
149
  <Rating value={item.rating} />
156
150
  </div>
157
- <div class="flex min-w-0 gap-2.5">
158
- <Icon
159
- name="quote"
160
- class="text-primary/30 mt-0.5 size-5 shrink-0"
161
- />
162
- <p class="text-foreground/80 min-w-0 text-sm leading-relaxed">
151
+ <div class="flex gap-2.5">
152
+ <QuoteIcon class="text-primary/30 mt-0.5 size-5 shrink-0" />
153
+ <p class="text-foreground/80 text-sm leading-relaxed">
163
154
  {item.quote}
164
155
  </p>
165
156
  </div>
@@ -1,12 +1,13 @@
1
1
  ---
2
2
  import type { ImageMetadata } from "astro"
3
3
  import { Image } from "astro:assets"
4
+ import ArrowRightIcon from "@lucide/astro/icons/arrow-right"
4
5
 
5
6
  import { cn } from "@/lib/utils"
6
7
  import { Badge } from "@/components/ui/badge"
7
8
  import { Button } from "@/components/ui/button"
8
- import { Icon } from "@/components/ui/icon"
9
9
  import { Section, SectionContainer } from "@/components/ui/section"
10
+ import PlaceholderImage from "@/assets/placeholder.webp"
10
11
 
11
12
  type Props = {
12
13
  class?: string
@@ -18,7 +19,7 @@ type Props = {
18
19
  variant?: "default" | "outline" | "secondary" | "ghost"
19
20
  }[]
20
21
  services: {
21
- image: {
22
+ image?: {
22
23
  src: ImageMetadata
23
24
  alt: string
24
25
  }
@@ -70,9 +71,9 @@ const { class: className, badge, title, buttons, services } = Astro.props
70
71
  )}
71
72
  >
72
73
  <Image
73
- src={service.image.src}
74
- alt={service.image.alt}
75
- class="aspect-4/3 w-full object-cover"
74
+ src={service.image?.src ?? PlaceholderImage}
75
+ alt={service.image?.alt ?? service.title}
76
+ class="aspect-[4/3] w-full object-cover"
76
77
  widths={[320, 480, 640]}
77
78
  sizes="(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw"
78
79
  />
@@ -88,10 +89,7 @@ const { class: className, badge, title, buttons, services } = Astro.props
88
89
  </p>
89
90
  {service.href && (
90
91
  <span class="mt-auto pt-2">
91
- <Icon
92
- name="arrow-right"
93
- class="text-muted-foreground size-4 transition-transform group-hover/service:translate-x-1"
94
- />
92
+ <ArrowRightIcon class="text-muted-foreground size-4 transition-transform group-hover/service:translate-x-1" />
95
93
  </span>
96
94
  )}
97
95
  </div>
@@ -6,6 +6,7 @@ import { cn } from "@/lib/utils"
6
6
  import { Badge } from "@/components/ui/badge"
7
7
  import { Button } from "@/components/ui/button"
8
8
  import { Section, SectionContainer } from "@/components/ui/section"
9
+ import PlaceholderImage from "@/assets/placeholder.webp"
9
10
 
10
11
  type Props = {
11
12
  class?: string
@@ -18,7 +19,7 @@ type Props = {
18
19
  variant?: "default" | "outline" | "secondary" | "ghost"
19
20
  }[]
20
21
  services: {
21
- image: {
22
+ image?: {
22
23
  src: ImageMetadata
23
24
  alt: string
24
25
  }
@@ -75,9 +76,9 @@ const {
75
76
  services.map((service) => (
76
77
  <div class="group/service grid items-center gap-6 rounded-xl border p-4 shadow-xs transition-shadow hover:shadow-md md:grid-cols-[16rem_minmax(0,1fr)] md:p-0">
77
78
  <Image
78
- src={service.image.src}
79
- alt={service.image.alt}
80
- class="aspect-4/3 w-full rounded-lg object-cover md:aspect-auto md:h-full md:rounded-l-xl md:rounded-r-none"
79
+ src={service.image?.src ?? PlaceholderImage}
80
+ alt={service.image?.alt ?? service.title}
81
+ class="aspect-[4/3] w-full rounded-lg object-cover md:aspect-auto md:h-full md:rounded-l-xl md:rounded-r-none"
81
82
  widths={[320, 480]}
82
83
  sizes="(min-width: 768px) 16rem, 100vw"
83
84
  />
@@ -6,6 +6,7 @@ import { cn } from "@/lib/utils"
6
6
  import { Badge } from "@/components/ui/badge"
7
7
  import { Button } from "@/components/ui/button"
8
8
  import { Section, SectionContainer } from "@/components/ui/section"
9
+ import PlaceholderImage from "@/assets/placeholder.webp"
9
10
 
10
11
  type Props = {
11
12
  class?: string
@@ -18,7 +19,7 @@ type Props = {
18
19
  variant?: "default" | "outline" | "secondary" | "ghost"
19
20
  }[]
20
21
  services: {
21
- image: {
22
+ image?: {
22
23
  src: ImageMetadata
23
24
  alt: string
24
25
  }
@@ -72,15 +73,15 @@ const {
72
73
  <div class="grid gap-6 sm:grid-cols-2">
73
74
  {
74
75
  services.map((service) => (
75
- <div class="group/service relative isolate flex aspect-4/3 flex-col justify-end overflow-hidden rounded-xl">
76
+ <div class="group/service relative isolate flex aspect-[4/3] flex-col justify-end overflow-hidden rounded-xl">
76
77
  <Image
77
- src={service.image.src}
78
- alt={service.image.alt}
78
+ src={service.image?.src ?? PlaceholderImage}
79
+ alt={service.image?.alt ?? service.title}
79
80
  class="absolute inset-0 -z-10 size-full object-cover transition-transform duration-500 group-hover/service:scale-105"
80
81
  widths={[480, 640, 960]}
81
82
  sizes="(min-width: 640px) 50vw, 100vw"
82
83
  />
83
- <div class="absolute inset-0 -z-10 bg-linear-to-t from-black/80 via-black/40 to-transparent" />
84
+ <div class="absolute inset-0 -z-10 bg-gradient-to-t from-black/80 via-black/40 to-transparent" />
84
85
  <div class="flex flex-col gap-2 p-6 text-white">
85
86
  <h3 class="text-xl font-semibold">{service.title}</h3>
86
87
  <p class="text-sm leading-relaxed text-white/80">
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  import type { ImageMetadata } from "astro"
3
3
  import { Image } from "astro:assets"
4
+ import ArrowRightIcon from "@lucide/astro/icons/arrow-right"
4
5
 
5
6
  import { cn } from "@/lib/utils"
6
7
  import { Button } from "@/components/ui/button"
7
- import { Icon } from "@/components/ui/icon"
8
8
  import { Section, SectionContainer } from "@/components/ui/section"
9
+ import PlaceholderImage from "@/assets/placeholder.webp"
9
10
 
10
11
  type Props = {
11
12
  class?: string
@@ -17,7 +18,7 @@ type Props = {
17
18
  variant?: "default" | "outline" | "secondary" | "ghost"
18
19
  }[]
19
20
  services: {
20
- image: {
21
+ image?: {
21
22
  src: ImageMetadata
22
23
  alt: string
23
24
  }
@@ -66,9 +67,9 @@ const { class: className, title, description, buttons, services } = Astro.props
66
67
  services.map((service) => (
67
68
  <article class="group/service flex flex-col gap-4">
68
69
  <Image
69
- src={service.image.src}
70
- alt={service.image.alt}
71
- class="aspect-16/10 w-full rounded-lg object-cover"
70
+ src={service.image?.src ?? PlaceholderImage}
71
+ alt={service.image?.alt ?? service.title}
72
+ class="aspect-[16/10] w-full rounded-lg object-cover"
72
73
  widths={[320, 480, 640]}
73
74
  sizes="(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw"
74
75
  />
@@ -84,10 +85,7 @@ const { class: className, title, description, buttons, services } = Astro.props
84
85
  class="text-primary inline-flex items-center gap-1.5 text-sm font-medium"
85
86
  >
86
87
  Learn more
87
- <Icon
88
- name="arrow-right"
89
- class="size-3.5 transition-transform group-hover/service:translate-x-1"
90
- />
88
+ <ArrowRightIcon class="size-3.5 transition-transform group-hover/service:translate-x-1" />
91
89
  </a>
92
90
  )}
93
91
  </article>
@@ -1,4 +1,6 @@
1
1
  ---
2
+ import ArrowRightIcon from "@lucide/astro/icons/arrow-right"
3
+
2
4
  import { cn } from "@/lib/utils"
3
5
  import { Badge } from "@/components/ui/badge"
4
6
  import { Button } from "@/components/ui/button"
@@ -82,10 +84,7 @@ const {
82
84
  class="text-primary mt-auto inline-flex items-center gap-1.5 text-sm font-medium"
83
85
  >
84
86
  Learn more
85
- <Icon
86
- name="arrow-right"
87
- class="size-3.5 transition-transform group-hover/service:translate-x-1"
88
- />
87
+ <ArrowRightIcon class="size-3.5 transition-transform group-hover/service:translate-x-1" />
89
88
  </a>
90
89
  )}
91
90
  </div>
@@ -1,12 +1,13 @@
1
1
  ---
2
2
  import type { ImageMetadata } from "astro"
3
3
  import { Image } from "astro:assets"
4
+ import ArrowRightIcon from "@lucide/astro/icons/arrow-right"
4
5
 
5
6
  import { cn } from "@/lib/utils"
6
7
  import { Badge } from "@/components/ui/badge"
7
8
  import { Button } from "@/components/ui/button"
8
- import { Icon } from "@/components/ui/icon"
9
9
  import { Section, SectionContainer } from "@/components/ui/section"
10
+ import PlaceholderImage from "@/assets/placeholder.webp"
10
11
 
11
12
  type Props = {
12
13
  class?: string
@@ -19,7 +20,7 @@ type Props = {
19
20
  variant?: "default" | "outline" | "secondary" | "ghost"
20
21
  }[]
21
22
  services: {
22
- image: {
23
+ image?: {
23
24
  src: ImageMetadata
24
25
  alt: string
25
26
  }
@@ -72,13 +73,13 @@ const {
72
73
  services.map((service) => (
73
74
  <div class="group/service relative isolate flex aspect-square flex-col justify-end overflow-hidden rounded-xl bg-black">
74
75
  <Image
75
- src={service.image.src}
76
- alt={service.image.alt}
76
+ src={service.image?.src ?? PlaceholderImage}
77
+ alt={service.image?.alt ?? service.title}
77
78
  class="absolute inset-0 -z-10 size-full object-cover opacity-60 transition-transform duration-500 group-hover/service:scale-105"
78
79
  widths={[320, 480, 640]}
79
80
  sizes="(min-width: 1024px) 30vw, (min-width: 640px) 50vw, 100vw"
80
81
  />
81
- <div class="absolute inset-0 -z-10 bg-linear-to-t from-black/70 to-transparent" />
82
+ <div class="absolute inset-0 -z-10 bg-gradient-to-t from-black/70 to-transparent" />
82
83
  <div class="flex items-end justify-between gap-4 p-5 text-white">
83
84
  <div class="flex flex-col gap-1">
84
85
  <h3 class="text-lg font-semibold">{service.title}</h3>
@@ -91,7 +92,7 @@ const {
91
92
  href={service.href}
92
93
  class="shrink-0 rounded-full bg-white/15 p-2 backdrop-blur-sm transition-colors hover:bg-white/25"
93
94
  >
94
- <Icon name="arrow-right" class="size-4 text-white" />
95
+ <ArrowRightIcon class="size-4 text-white" />
95
96
  </a>
96
97
  )}
97
98
  </div>
@@ -1,12 +1,13 @@
1
1
  ---
2
2
  import type { ImageMetadata } from "astro"
3
3
  import { Image } from "astro:assets"
4
+ import ArrowRightIcon from "@lucide/astro/icons/arrow-right"
4
5
 
5
6
  import { cn } from "@/lib/utils"
6
7
  import { Badge } from "@/components/ui/badge"
7
8
  import { Button } from "@/components/ui/button"
8
- import { Icon } from "@/components/ui/icon"
9
9
  import { Section, SectionContainer } from "@/components/ui/section"
10
+ import PlaceholderImage from "@/assets/placeholder.webp"
10
11
 
11
12
  type Props = {
12
13
  class?: string
@@ -19,7 +20,7 @@ type Props = {
19
20
  variant?: "default" | "outline" | "secondary" | "ghost"
20
21
  }[]
21
22
  services: {
22
- image: {
23
+ image?: {
23
24
  src: ImageMetadata
24
25
  alt: string
25
26
  }
@@ -83,11 +84,11 @@ const {
83
84
  )}
84
85
  >
85
86
  <Image
86
- src={service.image.src}
87
- alt={service.image.alt}
87
+ src={service.image?.src ?? PlaceholderImage}
88
+ alt={service.image?.alt ?? service.title}
88
89
  class={cn(
89
90
  "w-full object-cover",
90
- index === 0 ? "aspect-video" : "aspect-16/10"
91
+ index === 0 ? "aspect-[16/9]" : "aspect-[16/10]"
91
92
  )}
92
93
  widths={[320, 480, 640, 960]}
93
94
  sizes={
@@ -117,10 +118,7 @@ const {
117
118
  )}
118
119
  >
119
120
  Learn more
120
- <Icon
121
- name="arrow-right"
122
- class="size-3.5 transition-transform group-hover/service:translate-x-1"
123
- />
121
+ <ArrowRightIcon class="size-3.5 transition-transform group-hover/service:translate-x-1" />
124
122
  </div>
125
123
  </div>
126
124
  </a>