@shipsite.dev/components 0.2.21 → 0.2.23

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 (127) hide show
  1. package/components.json +31 -11
  2. package/dist/blog/BlogArticle.d.ts +2 -1
  3. package/dist/blog/BlogArticle.d.ts.map +1 -1
  4. package/dist/blog/BlogArticle.js +2 -2
  5. package/dist/blog/BlogArticle.js.map +1 -1
  6. package/dist/blog/BlogArticleClient.d.ts +4 -2
  7. package/dist/blog/BlogArticleClient.d.ts.map +1 -1
  8. package/dist/blog/BlogArticleClient.js +4 -3
  9. package/dist/blog/BlogArticleClient.js.map +1 -1
  10. package/dist/blog/BlogIndex.d.ts +2 -1
  11. package/dist/blog/BlogIndex.d.ts.map +1 -1
  12. package/dist/blog/BlogIndex.js +2 -2
  13. package/dist/blog/BlogIndex.js.map +1 -1
  14. package/dist/content/ContentPage.d.ts +2 -1
  15. package/dist/content/ContentPage.d.ts.map +1 -1
  16. package/dist/content/ContentPage.js +2 -2
  17. package/dist/content/ContentPage.js.map +1 -1
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/marketing/AlternatingFeatures.d.ts +5 -4
  23. package/dist/marketing/AlternatingFeatures.d.ts.map +1 -1
  24. package/dist/marketing/AlternatingFeatures.js +6 -5
  25. package/dist/marketing/AlternatingFeatures.js.map +1 -1
  26. package/dist/marketing/BannerCTA.d.ts +2 -1
  27. package/dist/marketing/BannerCTA.d.ts.map +1 -1
  28. package/dist/marketing/BannerCTA.js +2 -2
  29. package/dist/marketing/BannerCTA.js.map +1 -1
  30. package/dist/marketing/BentoGrid.d.ts +4 -2
  31. package/dist/marketing/BentoGrid.d.ts.map +1 -1
  32. package/dist/marketing/BentoGrid.js +4 -3
  33. package/dist/marketing/BentoGrid.js.map +1 -1
  34. package/dist/marketing/CalloutCard.d.ts +2 -1
  35. package/dist/marketing/CalloutCard.d.ts.map +1 -1
  36. package/dist/marketing/CalloutCard.js +2 -2
  37. package/dist/marketing/CalloutCard.js.map +1 -1
  38. package/dist/marketing/CardGrid.d.ts +2 -1
  39. package/dist/marketing/CardGrid.d.ts.map +1 -1
  40. package/dist/marketing/CardGrid.js +2 -2
  41. package/dist/marketing/CardGrid.js.map +1 -1
  42. package/dist/marketing/Carousel.d.ts +4 -2
  43. package/dist/marketing/Carousel.d.ts.map +1 -1
  44. package/dist/marketing/Carousel.js +4 -3
  45. package/dist/marketing/Carousel.js.map +1 -1
  46. package/dist/marketing/Companies.d.ts +4 -2
  47. package/dist/marketing/Companies.d.ts.map +1 -1
  48. package/dist/marketing/Companies.js +4 -3
  49. package/dist/marketing/Companies.js.map +1 -1
  50. package/dist/marketing/FAQ.d.ts +2 -1
  51. package/dist/marketing/FAQ.d.ts.map +1 -1
  52. package/dist/marketing/FAQ.js +2 -2
  53. package/dist/marketing/FAQ.js.map +1 -1
  54. package/dist/marketing/Features.d.ts +2 -1
  55. package/dist/marketing/Features.d.ts.map +1 -1
  56. package/dist/marketing/Features.js +2 -2
  57. package/dist/marketing/Features.js.map +1 -1
  58. package/dist/marketing/Gallery.d.ts +4 -2
  59. package/dist/marketing/Gallery.d.ts.map +1 -1
  60. package/dist/marketing/Gallery.js +4 -3
  61. package/dist/marketing/Gallery.js.map +1 -1
  62. package/dist/marketing/Hero.d.ts +4 -2
  63. package/dist/marketing/Hero.d.ts.map +1 -1
  64. package/dist/marketing/Hero.js +3 -2
  65. package/dist/marketing/Hero.js.map +1 -1
  66. package/dist/marketing/PageHero.d.ts +2 -1
  67. package/dist/marketing/PageHero.d.ts.map +1 -1
  68. package/dist/marketing/PageHero.js +2 -2
  69. package/dist/marketing/PageHero.js.map +1 -1
  70. package/dist/marketing/PricingSection.d.ts +2 -1
  71. package/dist/marketing/PricingSection.d.ts.map +1 -1
  72. package/dist/marketing/PricingSection.js +2 -2
  73. package/dist/marketing/PricingSection.js.map +1 -1
  74. package/dist/marketing/SocialProof.d.ts +2 -1
  75. package/dist/marketing/SocialProof.d.ts.map +1 -1
  76. package/dist/marketing/SocialProof.js +2 -2
  77. package/dist/marketing/SocialProof.js.map +1 -1
  78. package/dist/marketing/Stats.d.ts +2 -1
  79. package/dist/marketing/Stats.d.ts.map +1 -1
  80. package/dist/marketing/Stats.js +2 -2
  81. package/dist/marketing/Stats.js.map +1 -1
  82. package/dist/marketing/Steps.d.ts +2 -1
  83. package/dist/marketing/Steps.d.ts.map +1 -1
  84. package/dist/marketing/Steps.js +2 -2
  85. package/dist/marketing/Steps.js.map +1 -1
  86. package/dist/marketing/TabsSection.d.ts +4 -2
  87. package/dist/marketing/TabsSection.d.ts.map +1 -1
  88. package/dist/marketing/TabsSection.js +4 -3
  89. package/dist/marketing/TabsSection.js.map +1 -1
  90. package/dist/marketing/Testimonial.d.ts +4 -2
  91. package/dist/marketing/Testimonial.d.ts.map +1 -1
  92. package/dist/marketing/Testimonial.js +3 -2
  93. package/dist/marketing/Testimonial.js.map +1 -1
  94. package/dist/marketing/Testimonials.d.ts +4 -2
  95. package/dist/marketing/Testimonials.d.ts.map +1 -1
  96. package/dist/marketing/Testimonials.js +4 -3
  97. package/dist/marketing/Testimonials.js.map +1 -1
  98. package/dist/ui/theme-image.d.ts +11 -0
  99. package/dist/ui/theme-image.d.ts.map +1 -0
  100. package/dist/ui/theme-image.js +9 -0
  101. package/dist/ui/theme-image.js.map +1 -0
  102. package/package.json +1 -1
  103. package/src/blog/BlogArticle.tsx +3 -2
  104. package/src/blog/BlogArticleClient.tsx +6 -3
  105. package/src/blog/BlogIndex.tsx +3 -2
  106. package/src/content/ContentPage.tsx +3 -2
  107. package/src/index.ts +4 -0
  108. package/src/marketing/AlternatingFeatures.tsx +7 -13
  109. package/src/marketing/BannerCTA.tsx +3 -2
  110. package/src/marketing/BentoGrid.tsx +6 -4
  111. package/src/marketing/CalloutCard.tsx +3 -2
  112. package/src/marketing/CardGrid.tsx +3 -2
  113. package/src/marketing/Carousel.tsx +6 -4
  114. package/src/marketing/Companies.tsx +10 -8
  115. package/src/marketing/FAQ.tsx +3 -2
  116. package/src/marketing/Features.tsx +3 -2
  117. package/src/marketing/Gallery.tsx +6 -4
  118. package/src/marketing/Hero.tsx +6 -4
  119. package/src/marketing/PageHero.tsx +3 -2
  120. package/src/marketing/PricingSection.tsx +3 -2
  121. package/src/marketing/SocialProof.tsx +3 -2
  122. package/src/marketing/Stats.tsx +3 -2
  123. package/src/marketing/Steps.tsx +3 -2
  124. package/src/marketing/TabsSection.tsx +6 -4
  125. package/src/marketing/Testimonial.tsx +6 -4
  126. package/src/marketing/Testimonials.tsx +6 -4
  127. package/src/ui/theme-image.tsx +20 -0
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Section } from '../ui/section';
3
3
  import { Mockup } from '../ui/mockup';
4
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
4
5
 
5
6
  interface AlternatingFeatureItemProps {
6
7
  icon?: string;
@@ -23,13 +24,12 @@ export function AlternatingFeatureItem({ icon, title, description }: Alternating
23
24
  interface AlternatingFeatureRowProps {
24
25
  title: string;
25
26
  description?: string;
26
- image: string;
27
- imageDark?: string;
27
+ image: ImageSource;
28
28
  imageAlt?: string;
29
29
  children?: React.ReactNode;
30
30
  }
31
31
 
32
- export function AlternatingFeatureRow({ title, description, image, imageDark, imageAlt, children }: AlternatingFeatureRowProps) {
32
+ export function AlternatingFeatureRow({ title, description, image, imageAlt, children }: AlternatingFeatureRowProps) {
33
33
  return (
34
34
  <div className="grid grid-cols-1 md:grid-cols-2 gap-12 items-center py-12 [&:nth-child(even)>div:first-child]:md:order-2">
35
35
  <div>
@@ -38,28 +38,22 @@ export function AlternatingFeatureRow({ title, description, image, imageDark, im
38
38
  {children && <div className="space-y-4">{children}</div>}
39
39
  </div>
40
40
  <Mockup type="responsive">
41
- {imageDark ? (
42
- <>
43
- <img src={image} alt={imageAlt || title} className="w-full dark:hidden" />
44
- <img src={imageDark} alt={imageAlt || title} className="w-full hidden dark:block" />
45
- </>
46
- ) : (
47
- <img src={image} alt={imageAlt || title} className="w-full" />
48
- )}
41
+ <ThemeImage src={image} alt={imageAlt || title} className="w-full" />
49
42
  </Mockup>
50
43
  </div>
51
44
  );
52
45
  }
53
46
 
54
47
  interface AlternatingFeaturesProps {
48
+ id?: string;
55
49
  title?: string;
56
50
  description?: string;
57
51
  children: React.ReactNode;
58
52
  }
59
53
 
60
- export function AlternatingFeatures({ title, description, children }: AlternatingFeaturesProps) {
54
+ export function AlternatingFeatures({ id, title, description, children }: AlternatingFeaturesProps) {
61
55
  return (
62
- <Section>
56
+ <Section id={id}>
63
57
  <div className="container-main">
64
58
  {(title || description) && (
65
59
  <div className="text-center mb-16">
@@ -4,6 +4,7 @@ import { Button } from '../ui/button';
4
4
  import Glow from '../ui/glow';
5
5
 
6
6
  interface BannerCTAProps {
7
+ id?: string;
7
8
  title: string;
8
9
  buttonText: string;
9
10
  buttonHref?: string;
@@ -11,9 +12,9 @@ interface BannerCTAProps {
11
12
  children?: React.ReactNode;
12
13
  }
13
14
 
14
- export function BannerCTA({ title, buttonText, buttonHref, subtext, children }: BannerCTAProps) {
15
+ export function BannerCTA({ id, title, buttonText, buttonHref, subtext, children }: BannerCTAProps) {
15
16
  return (
16
- <Section>
17
+ <Section id={id}>
17
18
  <div className="container-main">
18
19
  <div className="relative overflow-hidden glass-4 rounded-3xl p-12 md:p-16 text-center">
19
20
  <Glow variant="center" />
@@ -1,11 +1,12 @@
1
1
  import React from 'react';
2
2
  import { Section } from '../ui/section';
3
3
  import { cn } from '../lib/utils';
4
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
4
5
 
5
6
  interface BentoItemProps {
6
7
  title: string;
7
8
  description?: string;
8
- image?: string;
9
+ image?: ImageSource;
9
10
  span?: 1 | 2;
10
11
  children?: React.ReactNode;
11
12
  }
@@ -20,7 +21,7 @@ export function BentoItem({ title, description, image, span = 1, children }: Ben
20
21
  {description && <p className="text-sm text-muted-foreground mb-4 leading-relaxed">{description}</p>}
21
22
  {image && (
22
23
  <div className="mt-auto -mx-6 -mb-6 md:-mx-8 md:-mb-8">
23
- <img src={image} alt={title} className="w-full" />
24
+ <ThemeImage src={image} alt={title} className="w-full" />
24
25
  </div>
25
26
  )}
26
27
  {children}
@@ -29,14 +30,15 @@ export function BentoItem({ title, description, image, span = 1, children }: Ben
29
30
  }
30
31
 
31
32
  interface BentoGridProps {
33
+ id?: string;
32
34
  title?: string;
33
35
  description?: string;
34
36
  children: React.ReactNode;
35
37
  }
36
38
 
37
- export function BentoGrid({ title, description, children }: BentoGridProps) {
39
+ export function BentoGrid({ id, title, description, children }: BentoGridProps) {
38
40
  return (
39
- <Section>
41
+ <Section id={id}>
40
42
  <div className="container-main">
41
43
  {(title || description) && (
42
44
  <div className="text-center mb-12">
@@ -2,13 +2,14 @@ import React from 'react';
2
2
  import { cn } from '../lib/utils';
3
3
 
4
4
  interface CalloutCardProps {
5
+ id?: string;
5
6
  title: string;
6
7
  description: string;
7
8
  variant?: 'info' | 'success' | 'warning';
8
9
  children?: React.ReactNode;
9
10
  }
10
11
 
11
- export function CalloutCard({ title, description, variant = 'info', children }: CalloutCardProps) {
12
+ export function CalloutCard({ id, title, description, variant = 'info', children }: CalloutCardProps) {
12
13
  const accentStyles = {
13
14
  info: 'border-l-primary',
14
15
  success: 'border-l-emerald-500',
@@ -16,7 +17,7 @@ export function CalloutCard({ title, description, variant = 'info', children }:
16
17
  };
17
18
 
18
19
  return (
19
- <div className={cn('mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] my-8')}>
20
+ <div id={id} className={cn('mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] my-8')}>
20
21
  <div className={cn('glass-1 rounded-xl border-l-4 p-6 shadow-xl', accentStyles[variant])}>
21
22
  <h3 className="font-semibold tracking-tight text-foreground mb-2">{title}</h3>
22
23
  <p className="text-sm text-muted-foreground text-balance">{description}</p>
@@ -19,14 +19,15 @@ export function CardGridItem({ title, description, icon, href }: CardGridItemPro
19
19
  }
20
20
 
21
21
  interface CardGridProps {
22
+ id?: string;
22
23
  columns?: 2 | 3 | 4;
23
24
  children: React.ReactNode;
24
25
  }
25
26
 
26
- export function CardGrid({ columns = 3, children }: CardGridProps) {
27
+ export function CardGrid({ id, columns = 3, children }: CardGridProps) {
27
28
  const gridCols = { 2: 'md:grid-cols-2', 3: 'md:grid-cols-3', 4: 'md:grid-cols-2 lg:grid-cols-4' };
28
29
  return (
29
- <div className="mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] py-8">
30
+ <div id={id} className="mx-auto w-full max-w-[76rem] px-[clamp(1rem,3vw,3rem)] py-8">
30
31
  <div className={`grid grid-cols-1 ${gridCols[columns]} gap-6`}>{children}</div>
31
32
  </div>
32
33
  );
@@ -4,18 +4,19 @@ import React, { useRef } from 'react';
4
4
  import { ChevronLeft, ChevronRight } from 'lucide-react';
5
5
  import { Section } from '../ui/section';
6
6
  import { cn } from '../lib/utils';
7
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
7
8
 
8
9
  interface CarouselItemProps {
9
10
  title?: string;
10
11
  description?: string;
11
- image?: string;
12
+ image?: ImageSource;
12
13
  children?: React.ReactNode;
13
14
  }
14
15
 
15
16
  export function CarouselItem({ title, description, image, children }: CarouselItemProps) {
16
17
  return (
17
18
  <div className="glass-1 rounded-2xl overflow-hidden flex-shrink-0 w-[85vw] max-w-[400px] snap-center">
18
- {image && <img src={image} alt={title || ''} className="w-full aspect-video object-cover" />}
19
+ {image && <ThemeImage src={image} alt={title || ''} className="w-full aspect-video object-cover" />}
19
20
  <div className="p-6">
20
21
  {title && <h3 className="text-lg font-semibold text-foreground mb-2">{title}</h3>}
21
22
  {description && <p className="text-sm text-muted-foreground leading-relaxed">{description}</p>}
@@ -26,12 +27,13 @@ export function CarouselItem({ title, description, image, children }: CarouselIt
26
27
  }
27
28
 
28
29
  interface CarouselProps {
30
+ id?: string;
29
31
  title?: string;
30
32
  description?: string;
31
33
  children: React.ReactNode;
32
34
  }
33
35
 
34
- export function Carousel({ title, description, children }: CarouselProps) {
36
+ export function Carousel({ id, title, description, children }: CarouselProps) {
35
37
  const scrollRef = useRef<HTMLDivElement>(null);
36
38
 
37
39
  const scroll = (direction: 'left' | 'right') => {
@@ -44,7 +46,7 @@ export function Carousel({ title, description, children }: CarouselProps) {
44
46
  };
45
47
 
46
48
  return (
47
- <Section>
49
+ <Section id={id}>
48
50
  <div className="container-main">
49
51
  {(title || description) && (
50
52
  <div className="text-center mb-12">
@@ -2,9 +2,10 @@ import React from 'react';
2
2
  import { Section } from '../ui/section';
3
3
  import { Badge } from '../ui/badge';
4
4
  import { cn } from '../lib/utils';
5
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
5
6
 
6
7
  interface LogoItem {
7
- src: string;
8
+ src: ImageSource;
8
9
  alt: string;
9
10
  width?: number;
10
11
  name?: string;
@@ -13,26 +14,27 @@ interface LogoItem {
13
14
  }
14
15
 
15
16
  interface CompaniesProps {
17
+ id?: string;
16
18
  title?: string;
17
19
  logos: LogoItem[];
18
20
  variant?: 'marquee' | 'inline';
19
21
  }
20
22
 
21
- export function Companies({ title, logos, variant = 'marquee' }: CompaniesProps) {
23
+ export function Companies({ id, title, logos, variant = 'marquee' }: CompaniesProps) {
22
24
  if (variant === 'inline') {
23
25
  return (
24
- <Section className="py-12">
26
+ <Section id={id} className="py-12">
25
27
  <div className="container-main flex flex-col items-center gap-8 text-center">
26
28
  {title && <h2 className="text-base font-semibold sm:text-2xl text-foreground">{title}</h2>}
27
29
  <div className="flex flex-wrap items-center justify-center gap-8">
28
30
  {logos.map((logo, i) => (
29
31
  <div key={i} className="flex items-center gap-2 text-sm font-medium">
30
- <img
32
+ <ThemeImage
31
33
  src={logo.src}
32
34
  alt={logo.alt}
33
35
  width={logo.width || 32}
34
36
  height={logo.width || 32}
35
- className="h-6 w-6 object-contain opacity-70 dark:invert"
37
+ className={cn("h-6 w-6 object-contain opacity-70", typeof logo.src === 'string' && "dark:invert")}
36
38
  />
37
39
  {logo.name && <span className={cn(!logo.name && 'sr-only')}>{logo.name}</span>}
38
40
  {logo.version && <span className="text-muted-foreground">{logo.version}</span>}
@@ -48,19 +50,19 @@ export function Companies({ title, logos, variant = 'marquee' }: CompaniesProps)
48
50
  }
49
51
 
50
52
  return (
51
- <Section className="py-12">
53
+ <Section id={id} className="py-12">
52
54
  <div className="container-main">
53
55
  {title && <p className="text-center text-sm text-muted-foreground mb-8">{title}</p>}
54
56
  <div className="relative fade-x overflow-hidden">
55
57
  <div className="flex gap-12 items-center" style={{ '--marquee-gap': '3rem' } as React.CSSProperties}>
56
58
  <div className="flex gap-12 items-center animate-marquee">
57
59
  {logos.map((logo, i) => (
58
- <img key={i} src={logo.src} alt={logo.alt} width={logo.width || 120} className="h-8 w-auto object-contain grayscale opacity-60 hover:grayscale-0 hover:opacity-100 transition-all" />
60
+ <ThemeImage key={i} src={logo.src} alt={logo.alt} width={logo.width || 120} className="h-8 w-auto object-contain grayscale opacity-60 hover:grayscale-0 hover:opacity-100 transition-all" />
59
61
  ))}
60
62
  </div>
61
63
  <div className="flex gap-12 items-center animate-marquee" aria-hidden>
62
64
  {logos.map((logo, i) => (
63
- <img key={i} src={logo.src} alt="" width={logo.width || 120} className="h-8 w-auto object-contain grayscale opacity-60" />
65
+ <ThemeImage key={i} src={logo.src} alt="" width={logo.width || 120} className="h-8 w-auto object-contain grayscale opacity-60" />
64
66
  ))}
65
67
  </div>
66
68
  </div>
@@ -26,14 +26,15 @@ export function FAQItem({ question, children }: FAQItemProps) {
26
26
  }
27
27
 
28
28
  interface FAQProps {
29
+ id?: string;
29
30
  title?: string;
30
31
  description?: string;
31
32
  children: React.ReactNode;
32
33
  }
33
34
 
34
- export function FAQ({ title, description, children }: FAQProps) {
35
+ export function FAQ({ id, title, description, children }: FAQProps) {
35
36
  return (
36
- <Section>
37
+ <Section id={id}>
37
38
  <div className="container-main max-w-3xl">
38
39
  {(title || description) && (
39
40
  <div className="text-center mb-12">
@@ -22,17 +22,18 @@ export function Feature({ icon, title, description }: FeatureProps) {
22
22
  }
23
23
 
24
24
  interface FeaturesProps {
25
+ id?: string;
25
26
  title?: string;
26
27
  description?: string;
27
28
  columns?: 2 | 3 | 4;
28
29
  children: React.ReactNode;
29
30
  }
30
31
 
31
- export function Features({ title, description, columns = 3, children }: FeaturesProps) {
32
+ export function Features({ id, title, description, columns = 3, children }: FeaturesProps) {
32
33
  const gridCols = { 2: 'md:grid-cols-2', 3: 'md:grid-cols-3', 4: 'md:grid-cols-2 lg:grid-cols-4' };
33
34
 
34
35
  return (
35
- <Section>
36
+ <Section id={id}>
36
37
  <div className="container-main">
37
38
  {(title || description) && (
38
39
  <div className="text-center mb-12">
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
  import { Section } from '../ui/section';
3
3
  import { cn } from '../lib/utils';
4
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
4
5
 
5
6
  interface GalleryItemProps {
6
- src: string;
7
+ src: ImageSource;
7
8
  alt: string;
8
9
  caption?: string;
9
10
  }
@@ -12,7 +13,7 @@ export function GalleryItem({ src, alt, caption }: GalleryItemProps) {
12
13
  return (
13
14
  <figure className="group overflow-hidden rounded-xl glass-1">
14
15
  <div className="overflow-hidden">
15
- <img
16
+ <ThemeImage
16
17
  src={src}
17
18
  alt={alt}
18
19
  className="w-full h-auto object-cover transition-transform duration-300 group-hover:scale-105"
@@ -26,13 +27,14 @@ export function GalleryItem({ src, alt, caption }: GalleryItemProps) {
26
27
  }
27
28
 
28
29
  interface GalleryProps {
30
+ id?: string;
29
31
  title?: string;
30
32
  description?: string;
31
33
  columns?: 2 | 3 | 4;
32
34
  children: React.ReactNode;
33
35
  }
34
36
 
35
- export function Gallery({ title, description, columns = 3, children }: GalleryProps) {
37
+ export function Gallery({ id, title, description, columns = 3, children }: GalleryProps) {
36
38
  const gridCols = {
37
39
  2: 'md:grid-cols-2',
38
40
  3: 'md:grid-cols-2 lg:grid-cols-3',
@@ -40,7 +42,7 @@ export function Gallery({ title, description, columns = 3, children }: GalleryPr
40
42
  };
41
43
 
42
44
  return (
43
- <Section>
45
+ <Section id={id}>
44
46
  <div className="container-main">
45
47
  {(title || description) && (
46
48
  <div className="text-center mb-12">
@@ -5,20 +5,22 @@ import { Badge } from '../ui/badge';
5
5
  import { Button } from '../ui/button';
6
6
  import { Mockup } from '../ui/mockup';
7
7
  import Glow from '../ui/glow';
8
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
8
9
 
9
10
  interface HeroProps {
11
+ id?: string;
10
12
  title: string;
11
13
  description: string;
12
14
  primaryCta?: { label: string; href: string };
13
15
  secondaryCta?: { label: string; href: string };
14
16
  badge?: string;
15
- image?: string;
17
+ image?: ImageSource;
16
18
  children?: React.ReactNode;
17
19
  }
18
20
 
19
- export function Hero({ title, description, primaryCta, secondaryCta, badge, image, children }: HeroProps) {
21
+ export function Hero({ id, title, description, primaryCta, secondaryCta, badge, image, children }: HeroProps) {
20
22
  return (
21
- <Section className="relative overflow-hidden">
23
+ <Section id={id} className="relative overflow-hidden">
22
24
  <Glow variant="top" />
23
25
  <div className="container-main relative z-10">
24
26
  <div className="max-w-3xl mx-auto text-center">
@@ -50,7 +52,7 @@ export function Hero({ title, description, primaryCta, secondaryCta, badge, imag
50
52
  {image && (
51
53
  <div className="mt-16 animate-appear-zoom [animation-delay:400ms]">
52
54
  <Mockup type="responsive" className="shadow-mockup w-full">
53
- <img src={image} alt="" className="w-full" />
55
+ <ThemeImage src={image} alt="" className="w-full" />
54
56
  </Mockup>
55
57
  </div>
56
58
  )}
@@ -3,15 +3,16 @@ import { Section } from '../ui/section';
3
3
  import { Badge } from '../ui/badge';
4
4
 
5
5
  interface PageHeroProps {
6
+ id?: string;
6
7
  title: string;
7
8
  description?: string;
8
9
  badge?: string;
9
10
  children?: React.ReactNode;
10
11
  }
11
12
 
12
- export function PageHero({ title, description, badge, children }: PageHeroProps) {
13
+ export function PageHero({ id, title, description, badge, children }: PageHeroProps) {
13
14
  return (
14
- <Section className="py-16 md:py-24">
15
+ <Section id={id} className="py-16 md:py-24">
15
16
  <div className="container-main text-center">
16
17
  {badge && (
17
18
  <Badge variant="outline" className="mb-4">
@@ -38,6 +38,7 @@ export function ComparisonCategory(_props: ComparisonCategoryProps) {
38
38
  }
39
39
 
40
40
  interface PricingSectionProps {
41
+ id?: string;
41
42
  title?: string;
42
43
  description?: string;
43
44
  monthlyLabel?: string;
@@ -46,7 +47,7 @@ interface PricingSectionProps {
46
47
  children: React.ReactNode;
47
48
  }
48
49
 
49
- export function PricingSection({ title, description, monthlyLabel = 'Monthly', yearlyLabel = 'Yearly', mostPopularLabel = 'Most Popular', children }: PricingSectionProps) {
50
+ export function PricingSection({ id, title, description, monthlyLabel = 'Monthly', yearlyLabel = 'Yearly', mostPopularLabel = 'Most Popular', children }: PricingSectionProps) {
50
51
  const [isYearly, setIsYearly] = useState(false);
51
52
 
52
53
  const plans: PricingPlanProps[] = [];
@@ -60,7 +61,7 @@ export function PricingSection({ title, description, monthlyLabel = 'Monthly', y
60
61
  });
61
62
 
62
63
  return (
63
- <Section>
64
+ <Section id={id}>
64
65
  <div className="container-main">
65
66
  {(title || description) && (
66
67
  <div className="text-center mb-12">
@@ -2,14 +2,15 @@ import React from 'react';
2
2
  import { Section } from '../ui/section';
3
3
 
4
4
  interface SocialProofProps {
5
+ id?: string;
5
6
  avatars?: string[];
6
7
  text: string;
7
8
  subtext?: string;
8
9
  }
9
10
 
10
- export function SocialProof({ avatars, text, subtext }: SocialProofProps) {
11
+ export function SocialProof({ id, avatars, text, subtext }: SocialProofProps) {
11
12
  return (
12
- <Section className="py-12">
13
+ <Section id={id} className="py-12">
13
14
  <div className="container-main">
14
15
  <div className="flex flex-col items-center gap-4 text-center">
15
16
  {avatars && avatars.length > 0 && (
@@ -13,11 +13,12 @@ export function Stat(_props: StatProps) {
13
13
  }
14
14
 
15
15
  interface StatsProps {
16
+ id?: string;
16
17
  title?: string;
17
18
  children: React.ReactNode;
18
19
  }
19
20
 
20
- export function Stats({ title, children }: StatsProps) {
21
+ export function Stats({ id, title, children }: StatsProps) {
21
22
  const items: StatProps[] = [];
22
23
  React.Children.forEach(children, (child) => {
23
24
  if (React.isValidElement(child) && child.type === Stat) {
@@ -26,7 +27,7 @@ export function Stats({ title, children }: StatsProps) {
26
27
  });
27
28
 
28
29
  return (
29
- <Section>
30
+ <Section id={id}>
30
31
  <div className="container-main max-w-[960px]">
31
32
  {title && (
32
33
  <h2 className="text-3xl md:text-4xl font-bold text-foreground mb-12 text-center">{title}</h2>
@@ -11,12 +11,13 @@ export function Step(_props: StepProps) {
11
11
  }
12
12
 
13
13
  interface StepsProps {
14
+ id?: string;
14
15
  title?: string;
15
16
  description?: string;
16
17
  children: React.ReactNode;
17
18
  }
18
19
 
19
- export function Steps({ title, description, children }: StepsProps) {
20
+ export function Steps({ id, title, description, children }: StepsProps) {
20
21
  const steps: StepProps[] = [];
21
22
  React.Children.forEach(children, (child) => {
22
23
  if (React.isValidElement(child) && child.type === Step) {
@@ -25,7 +26,7 @@ export function Steps({ title, description, children }: StepsProps) {
25
26
  });
26
27
 
27
28
  return (
28
- <Section>
29
+ <Section id={id}>
29
30
  <div className="container-main max-w-3xl">
30
31
  {(title || description) && (
31
32
  <div className="text-center mb-12">
@@ -3,12 +3,13 @@
3
3
  import React, { useState } from 'react';
4
4
  import { Section } from '../ui/section';
5
5
  import { cn } from '../lib/utils';
6
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
6
7
 
7
8
  interface TabItemProps {
8
9
  label: string;
9
10
  title?: string;
10
11
  description?: string;
11
- image?: string;
12
+ image?: ImageSource;
12
13
  children?: React.ReactNode;
13
14
  }
14
15
 
@@ -17,12 +18,13 @@ export function TabItem(_props: TabItemProps) {
17
18
  }
18
19
 
19
20
  interface TabsSectionProps {
21
+ id?: string;
20
22
  title?: string;
21
23
  description?: string;
22
24
  children: React.ReactNode;
23
25
  }
24
26
 
25
- export function TabsSection({ title, description, children }: TabsSectionProps) {
27
+ export function TabsSection({ id, title, description, children }: TabsSectionProps) {
26
28
  const [activeIndex, setActiveIndex] = useState(0);
27
29
 
28
30
  const tabs: TabItemProps[] = [];
@@ -35,7 +37,7 @@ export function TabsSection({ title, description, children }: TabsSectionProps)
35
37
  const activeTab = tabs[activeIndex];
36
38
 
37
39
  return (
38
- <Section>
40
+ <Section id={id}>
39
41
  <div className="container-main">
40
42
  {(title || description) && (
41
43
  <div className="text-center mb-12">
@@ -73,7 +75,7 @@ export function TabsSection({ title, description, children }: TabsSectionProps)
73
75
  )}
74
76
  </div>
75
77
  {activeTab.image && (
76
- <img src={activeTab.image} alt={activeTab.title || activeTab.label} className="w-full rounded-xl" />
78
+ <ThemeImage src={activeTab.image} alt={activeTab.title || activeTab.label} className="w-full rounded-xl" />
77
79
  )}
78
80
  </div>
79
81
  </div>
@@ -1,22 +1,24 @@
1
1
  import React from 'react';
2
2
  import { Section } from '../ui/section';
3
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
3
4
 
4
5
  interface TestimonialProps {
6
+ id?: string;
5
7
  quote: string;
6
8
  author: string;
7
9
  role?: string;
8
- image?: string;
10
+ image?: ImageSource;
9
11
  company?: string;
10
12
  }
11
13
 
12
- export function Testimonial({ quote, author, role, image, company }: TestimonialProps) {
14
+ export function Testimonial({ id, quote, author, role, image, company }: TestimonialProps) {
13
15
  return (
14
- <Section>
16
+ <Section id={id}>
15
17
  <div className="container-main max-w-3xl">
16
18
  <div className="glass-2 rounded-2xl p-8 md:p-12">
17
19
  <blockquote className="text-lg md:text-xl text-foreground/80 italic mb-6">&ldquo;{quote}&rdquo;</blockquote>
18
20
  <div className="flex items-center gap-3">
19
- {image && <img src={image} alt={author} className="w-10 h-10 rounded-full object-cover" />}
21
+ {image && <ThemeImage src={image} alt={author} className="w-10 h-10 rounded-full object-cover" />}
20
22
  <div>
21
23
  <p className="font-semibold text-foreground">{author}</p>
22
24
  {(role || company) && <p className="text-sm text-muted-foreground">{role}{role && company && ' \u00B7 '}{company}</p>}
@@ -1,12 +1,13 @@
1
1
  import React from 'react';
2
2
  import { Section } from '../ui/section';
3
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
3
4
 
4
5
  interface TestimonialCardProps {
5
6
  quote: string;
6
7
  author: string;
7
8
  role?: string;
8
9
  company?: string;
9
- image?: string;
10
+ image?: ImageSource;
10
11
  rating?: number;
11
12
  }
12
13
 
@@ -22,7 +23,7 @@ export function TestimonialCard({ quote, author, role, company, image, rating }:
22
23
  )}
23
24
  <blockquote className="text-sm text-foreground/80 leading-relaxed">&ldquo;{quote}&rdquo;</blockquote>
24
25
  <div className="flex items-center gap-3 mt-auto pt-2">
25
- {image && <img src={image} alt={author} className="w-8 h-8 rounded-full object-cover" />}
26
+ {image && <ThemeImage src={image} alt={author} className="w-8 h-8 rounded-full object-cover" />}
26
27
  <div>
27
28
  <p className="text-sm font-semibold text-foreground">{author}</p>
28
29
  {(role || company) && (
@@ -35,17 +36,18 @@ export function TestimonialCard({ quote, author, role, company, image, rating }:
35
36
  }
36
37
 
37
38
  interface TestimonialsProps {
39
+ id?: string;
38
40
  title?: string;
39
41
  description?: string;
40
42
  columns?: 2 | 3;
41
43
  children: React.ReactNode;
42
44
  }
43
45
 
44
- export function Testimonials({ title, description, columns = 3, children }: TestimonialsProps) {
46
+ export function Testimonials({ id, title, description, columns = 3, children }: TestimonialsProps) {
45
47
  const gridCols = columns === 2 ? 'md:grid-cols-2' : 'md:grid-cols-2 lg:grid-cols-3';
46
48
 
47
49
  return (
48
- <Section>
50
+ <Section id={id}>
49
51
  <div className="container-main">
50
52
  {(title || description) && (
51
53
  <div className="text-center mb-12">
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { cn } from '../lib/utils';
3
+
4
+ export type ImageSource = string | { light: string; dark: string };
5
+
6
+ interface ThemeImageProps extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, 'src'> {
7
+ src: ImageSource;
8
+ }
9
+
10
+ export function ThemeImage({ src, className, ...props }: ThemeImageProps) {
11
+ if (typeof src === 'string') {
12
+ return <img src={src} className={className} {...props} />;
13
+ }
14
+ return (
15
+ <>
16
+ <img src={src.light} className={cn(className, 'dark:hidden')} {...props} />
17
+ <img src={src.dark} className={cn(className, 'hidden dark:block')} {...props} />
18
+ </>
19
+ );
20
+ }