@shipsite.dev/components 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/package.json +2 -1
  2. package/src/blog/BlogArticle.tsx +14 -0
  3. package/src/blog/BlogCTA.tsx +19 -0
  4. package/src/blog/BlogCTABanner.tsx +25 -0
  5. package/src/blog/BlogFAQ.tsx +32 -0
  6. package/src/blog/BlogIndex.tsx +24 -0
  7. package/src/blog/BlogIntro.tsx +9 -0
  8. package/src/blog/BlogTable.tsx +27 -0
  9. package/src/blog/BlogTip.tsx +15 -0
  10. package/src/blog/StartFreeNowCTA.tsx +29 -0
  11. package/src/context/ShipSiteProvider.tsx +78 -0
  12. package/src/context/ThemeProvider.tsx +26 -0
  13. package/src/index.ts +63 -0
  14. package/src/layout/Footer.tsx +68 -0
  15. package/src/layout/Header.tsx +95 -0
  16. package/src/legal/LegalPage.tsx +35 -0
  17. package/src/lib/utils.ts +6 -0
  18. package/src/marketing/AlternatingFeatures.tsx +74 -0
  19. package/src/marketing/BannerCTA.tsx +43 -0
  20. package/src/marketing/BentoGrid.tsx +51 -0
  21. package/src/marketing/CalloutCard.tsx +25 -0
  22. package/src/marketing/CardGrid.tsx +29 -0
  23. package/src/marketing/Carousel.tsx +81 -0
  24. package/src/marketing/Companies.tsx +71 -0
  25. package/src/marketing/FAQ.tsx +50 -0
  26. package/src/marketing/Features.tsx +47 -0
  27. package/src/marketing/Gallery.tsx +55 -0
  28. package/src/marketing/Hero.tsx +60 -0
  29. package/src/marketing/PageHero.tsx +27 -0
  30. package/src/marketing/PricingSection.tsx +146 -0
  31. package/src/marketing/SocialProof.tsx +38 -0
  32. package/src/marketing/Stats.tsx +57 -0
  33. package/src/marketing/Steps.tsx +53 -0
  34. package/src/marketing/TabsSection.tsx +84 -0
  35. package/src/marketing/Testimonial.tsx +29 -0
  36. package/src/marketing/Testimonials.tsx +60 -0
  37. package/src/styles/utils.css +84 -0
  38. package/src/ui/accordion.tsx +66 -0
  39. package/src/ui/badge.tsx +55 -0
  40. package/src/ui/button.tsx +60 -0
  41. package/src/ui/card.tsx +75 -0
  42. package/src/ui/footer.tsx +51 -0
  43. package/src/ui/glow.tsx +48 -0
  44. package/src/ui/item.tsx +51 -0
  45. package/src/ui/mockup.tsx +64 -0
  46. package/src/ui/navbar.tsx +45 -0
  47. package/src/ui/section.tsx +15 -0
  48. package/src/ui/sheet.tsx +145 -0
  49. package/src/ui/theme-toggle.tsx +52 -0
@@ -0,0 +1,60 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../lib/utils";
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "text-primary-foreground shadow-sm dark:hover:from-primary/80 hover:from-primary/70 dark:hover:to-primary/70 hover:to-primary/90 bg-linear-to-b from-primary/60 to-primary/100 dark:from-primary/100 dark:to-primary/70 border-t-primary",
14
+ destructive:
15
+ "bg-destructive/30 text-destructive-foreground shadow-xs hover:bg-destructive/90",
16
+ outline:
17
+ "border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
18
+ glow: "glass-4 hover:glass-5 shadow-md",
19
+ secondary:
20
+ "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
21
+ ghost: "hover:bg-accent hover:text-accent-foreground",
22
+ link: "text-foreground underline-offset-4 hover:underline",
23
+ },
24
+ size: {
25
+ default: "h-9 px-4 py-2",
26
+ xs: "h-7 rounded-md px-2",
27
+ sm: "h-8 rounded-md px-3 text-xs",
28
+ lg: "h-10 rounded-md px-5",
29
+ icon: "size-9",
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ variant: "default",
34
+ size: "default",
35
+ },
36
+ },
37
+ );
38
+
39
+ function Button({
40
+ className,
41
+ variant,
42
+ size,
43
+ asChild = false,
44
+ ...props
45
+ }: React.ComponentProps<"button"> &
46
+ VariantProps<typeof buttonVariants> & {
47
+ asChild?: boolean;
48
+ }) {
49
+ const Comp = asChild ? Slot : "button";
50
+
51
+ return (
52
+ <Comp
53
+ data-slot="button"
54
+ className={cn(buttonVariants({ variant, size, className }))}
55
+ {...props}
56
+ />
57
+ );
58
+ }
59
+
60
+ export { Button, buttonVariants };
@@ -0,0 +1,75 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../lib/utils";
4
+
5
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="card"
9
+ className={cn(
10
+ "bg-card text-card-foreground rounded-xl border shadow-sm",
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <div
21
+ data-slot="card-header"
22
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ function CardTitle({ className, ...props }: React.ComponentProps<"h3">) {
29
+ return (
30
+ <h3
31
+ data-slot="card-title"
32
+ className={cn("leading-none font-semibold tracking-tight", className)}
33
+ {...props}
34
+ />
35
+ );
36
+ }
37
+
38
+ function CardDescription({ className, ...props }: React.ComponentProps<"p">) {
39
+ return (
40
+ <p
41
+ data-slot="card-description"
42
+ className={cn("text-muted-foreground text-sm", className)}
43
+ {...props}
44
+ />
45
+ );
46
+ }
47
+
48
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
49
+ return (
50
+ <div
51
+ data-slot="card-content"
52
+ className={cn("p-6 pt-0", className)}
53
+ {...props}
54
+ />
55
+ );
56
+ }
57
+
58
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
59
+ return (
60
+ <div
61
+ data-slot="card-footer"
62
+ className={cn("flex items-center p-6 pt-0", className)}
63
+ {...props}
64
+ />
65
+ );
66
+ }
67
+
68
+ export {
69
+ Card,
70
+ CardContent,
71
+ CardDescription,
72
+ CardFooter,
73
+ CardHeader,
74
+ CardTitle,
75
+ };
@@ -0,0 +1,51 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../lib/utils";
4
+
5
+ function FooterRoot({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="footer"
9
+ className={cn("bg-background text-foreground pt-12 pb-4", className)}
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ function FooterContent({ className, ...props }: React.ComponentProps<"div">) {
16
+ return (
17
+ <div
18
+ data-slot="footer-content"
19
+ className={cn(
20
+ "grid grid-cols-2 gap-8 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5",
21
+ className,
22
+ )}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ function FooterColumn({ className, ...props }: React.ComponentProps<"div">) {
29
+ return (
30
+ <div
31
+ data-slot="footer-column"
32
+ className={cn("flex flex-col gap-4", className)}
33
+ {...props}
34
+ />
35
+ );
36
+ }
37
+
38
+ function FooterBottom({ className, ...props }: React.ComponentProps<"div">) {
39
+ return (
40
+ <div
41
+ data-slot="footer-bottom"
42
+ className={cn(
43
+ "border-border dark:border-border/15 text-muted-foreground mt-8 flex flex-col items-center justify-between gap-4 border-t pt-4 text-xs sm:flex-row",
44
+ className,
45
+ )}
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+
51
+ export { FooterRoot, FooterBottom, FooterColumn, FooterContent };
@@ -0,0 +1,48 @@
1
+ import { cva, VariantProps } from "class-variance-authority";
2
+ import React from "react";
3
+
4
+ import { cn } from "../lib/utils";
5
+
6
+ const glowVariants = cva("absolute w-full", {
7
+ variants: {
8
+ variant: {
9
+ top: "top-0",
10
+ above: "-top-[128px]",
11
+ bottom: "bottom-0",
12
+ below: "-bottom-[128px]",
13
+ center: "top-[50%]",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "top",
18
+ },
19
+ });
20
+
21
+ function Glow({
22
+ className,
23
+ variant,
24
+ ...props
25
+ }: React.ComponentProps<"div"> & VariantProps<typeof glowVariants>) {
26
+ return (
27
+ <div
28
+ data-slot="glow"
29
+ className={cn(glowVariants({ variant }), className)}
30
+ {...props}
31
+ >
32
+ <div
33
+ className={cn(
34
+ "from-brand-foreground/50 to-brand-foreground/0 absolute left-1/2 h-[256px] w-[60%] -translate-x-1/2 scale-[2.5] rounded-[50%] bg-radial from-10% to-60% opacity-20 sm:h-[512px] dark:opacity-100",
35
+ variant === "center" && "-translate-y-1/2",
36
+ )}
37
+ />
38
+ <div
39
+ className={cn(
40
+ "from-brand/30 to-brand-foreground/0 absolute left-1/2 h-[128px] w-[40%] -translate-x-1/2 scale-200 rounded-[50%] bg-radial from-10% to-60% opacity-20 sm:h-[256px] dark:opacity-100",
41
+ variant === "center" && "-translate-y-1/2",
42
+ )}
43
+ />
44
+ </div>
45
+ );
46
+ }
47
+
48
+ export default Glow;
@@ -0,0 +1,51 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../lib/utils";
4
+
5
+ function Item({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="item"
9
+ className={cn("text-foreground flex flex-col gap-4 p-4", className)}
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ function ItemTitle({ className, ...props }: React.ComponentProps<"h3">) {
16
+ return (
17
+ <h3
18
+ data-slot="item-title"
19
+ className={cn(
20
+ "text-sm leading-none font-semibold tracking-tight sm:text-base",
21
+ className,
22
+ )}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ function ItemDescription({ className, ...props }: React.ComponentProps<"div">) {
29
+ return (
30
+ <div
31
+ data-slot="item-description"
32
+ className={cn(
33
+ "text-muted-foreground flex max-w-[240px] flex-col gap-2 text-sm text-balance",
34
+ className,
35
+ )}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function ItemIcon({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="item-icon"
45
+ className={cn("flex items-center self-start", className)}
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+
51
+ export { Item, ItemDescription, ItemIcon, ItemTitle };
@@ -0,0 +1,64 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import React from "react";
3
+
4
+ import { cn } from "../lib/utils";
5
+
6
+ const mockupVariants = cva(
7
+ "flex relative z-10 overflow-hidden shadow-2xl border border-border/70 dark:border-border/5 dark:border-t-border/15",
8
+ {
9
+ variants: {
10
+ type: {
11
+ mobile: "rounded-[48px] max-w-[350px]",
12
+ responsive: "rounded-md",
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ type: "responsive",
17
+ },
18
+ },
19
+ );
20
+
21
+ export interface MockupProps
22
+ extends React.HTMLAttributes<HTMLDivElement>,
23
+ VariantProps<typeof mockupVariants> {}
24
+
25
+ function Mockup({ className, type, ...props }: MockupProps) {
26
+ return (
27
+ <div
28
+ data-slot="mockup"
29
+ className={cn(mockupVariants({ type, className }))}
30
+ {...props}
31
+ />
32
+ );
33
+ }
34
+
35
+ const frameVariants = cva(
36
+ "bg-border/50 flex relative z-10 overflow-hidden rounded-2xl dark:bg-border/10",
37
+ {
38
+ variants: {
39
+ size: {
40
+ small: "p-2",
41
+ large: "p-4",
42
+ },
43
+ },
44
+ defaultVariants: {
45
+ size: "small",
46
+ },
47
+ },
48
+ );
49
+
50
+ export interface MockupFrameProps
51
+ extends React.HTMLAttributes<HTMLDivElement>,
52
+ VariantProps<typeof frameVariants> {}
53
+
54
+ function MockupFrame({ className, size, ...props }: MockupFrameProps) {
55
+ return (
56
+ <div
57
+ data-slot="mockup-frame"
58
+ className={cn(frameVariants({ size, className }))}
59
+ {...props}
60
+ />
61
+ );
62
+ }
63
+
64
+ export { Mockup, MockupFrame };
@@ -0,0 +1,45 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../lib/utils";
4
+
5
+ function Navbar({ className, ...props }: React.ComponentProps<"nav">) {
6
+ return (
7
+ <nav
8
+ data-slot="navbar"
9
+ className={cn("flex items-center justify-between py-4", className)}
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ function NavbarLeft({ className, ...props }: React.ComponentProps<"nav">) {
16
+ return (
17
+ <nav
18
+ data-slot="navbar-left"
19
+ className={cn("flex items-center justify-start gap-4", className)}
20
+ {...props}
21
+ />
22
+ );
23
+ }
24
+
25
+ function NavbarRight({ className, ...props }: React.ComponentProps<"nav">) {
26
+ return (
27
+ <nav
28
+ data-slot="navbar-right"
29
+ className={cn("flex items-center justify-end gap-4", className)}
30
+ {...props}
31
+ />
32
+ );
33
+ }
34
+
35
+ function NavbarCenter({ className, ...props }: React.ComponentProps<"nav">) {
36
+ return (
37
+ <nav
38
+ data-slot="navbar-center"
39
+ className={cn("flex items-center justify-center gap-4", className)}
40
+ {...props}
41
+ />
42
+ );
43
+ }
44
+
45
+ export { Navbar, NavbarCenter, NavbarLeft, NavbarRight };
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../lib/utils";
4
+
5
+ function Section({ className, ...props }: React.ComponentProps<"section">) {
6
+ return (
7
+ <section
8
+ data-slot="section"
9
+ className={cn("line-b px-4 py-12 sm:py-24 md:py-32", className)}
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ export { Section };
@@ -0,0 +1,145 @@
1
+ "use client";
2
+
3
+ import * as SheetPrimitive from "@radix-ui/react-dialog";
4
+ import { XIcon } from "lucide-react";
5
+ import * as React from "react";
6
+
7
+ import { cn } from "../lib/utils";
8
+
9
+ function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
10
+ return <SheetPrimitive.Root data-slot="sheet" {...props} />;
11
+ }
12
+
13
+ function SheetTrigger({
14
+ ...props
15
+ }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
16
+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
17
+ }
18
+
19
+ function SheetClose({
20
+ ...props
21
+ }: React.ComponentProps<typeof SheetPrimitive.Close>) {
22
+ return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
23
+ }
24
+
25
+ function SheetPortal({
26
+ ...props
27
+ }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
28
+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
29
+ }
30
+
31
+ function SheetOverlay({
32
+ className,
33
+ ...props
34
+ }: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
35
+ return (
36
+ <SheetPrimitive.Overlay
37
+ data-slot="sheet-overlay"
38
+ className={cn(
39
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
40
+ className,
41
+ )}
42
+ {...props}
43
+ />
44
+ );
45
+ }
46
+
47
+ function SheetContent({
48
+ className,
49
+ children,
50
+ side = "right",
51
+ ...props
52
+ }: React.ComponentProps<typeof SheetPrimitive.Content> & {
53
+ side?: "top" | "right" | "bottom" | "left";
54
+ }) {
55
+ return (
56
+ <SheetPortal>
57
+ <SheetOverlay />
58
+ <SheetPrimitive.Content
59
+ data-slot="sheet-content"
60
+ className={cn(
61
+ "border-border dark:border-border/15 bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 gap-4 p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
62
+ side === "right" &&
63
+ "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
64
+ side === "left" &&
65
+ "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
66
+ side === "top" &&
67
+ "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 border-b",
68
+ side === "bottom" &&
69
+ "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 border-t",
70
+ className,
71
+ )}
72
+ {...props}
73
+ >
74
+ {children}
75
+ <SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-6 right-6 z-[100] rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
76
+ <XIcon className="size-5" />
77
+ <span className="sr-only">Close</span>
78
+ </SheetPrimitive.Close>
79
+ </SheetPrimitive.Content>
80
+ </SheetPortal>
81
+ );
82
+ }
83
+
84
+ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
85
+ return (
86
+ <div
87
+ data-slot="sheet-header"
88
+ className={cn(
89
+ "flex flex-col space-y-2 text-center sm:text-left",
90
+ className,
91
+ )}
92
+ {...props}
93
+ />
94
+ );
95
+ }
96
+
97
+ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
98
+ return (
99
+ <div
100
+ data-slot="sheet-footer"
101
+ className={cn(
102
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
103
+ className,
104
+ )}
105
+ {...props}
106
+ />
107
+ );
108
+ }
109
+
110
+ function SheetTitle({
111
+ className,
112
+ ...props
113
+ }: React.ComponentProps<typeof SheetPrimitive.Title>) {
114
+ return (
115
+ <SheetPrimitive.Title
116
+ data-slot="sheet-title"
117
+ className={cn("text-foreground text-lg font-semibold", className)}
118
+ {...props}
119
+ />
120
+ );
121
+ }
122
+
123
+ function SheetDescription({
124
+ className,
125
+ ...props
126
+ }: React.ComponentProps<typeof SheetPrimitive.Description>) {
127
+ return (
128
+ <SheetPrimitive.Description
129
+ data-slot="sheet-description"
130
+ className={cn("text-muted-foreground text-sm", className)}
131
+ {...props}
132
+ />
133
+ );
134
+ }
135
+
136
+ export {
137
+ Sheet,
138
+ SheetClose,
139
+ SheetContent,
140
+ SheetDescription,
141
+ SheetFooter,
142
+ SheetHeader,
143
+ SheetTitle,
144
+ SheetTrigger,
145
+ };
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+
3
+ import React, { useEffect, useState } from 'react';
4
+ import { Moon, Sun, Monitor } from 'lucide-react';
5
+ import { useTheme } from 'next-themes';
6
+ import { cn } from '../lib/utils';
7
+
8
+ export function ThemeToggle({ className }: { className?: string }) {
9
+ const [mounted, setMounted] = useState(false);
10
+ const { theme, setTheme } = useTheme();
11
+
12
+ useEffect(() => {
13
+ setMounted(true);
14
+ }, []);
15
+
16
+ if (!mounted) return null;
17
+
18
+ return (
19
+ <div className={cn('flex items-center gap-0.5 rounded-full border border-border p-0.5', className)}>
20
+ <button
21
+ onClick={() => setTheme('light')}
22
+ className={cn(
23
+ 'rounded-full p-1.5 transition-colors',
24
+ theme === 'light' ? 'bg-muted text-foreground' : 'text-muted-foreground hover:text-foreground',
25
+ )}
26
+ aria-label="Light theme"
27
+ >
28
+ <Sun className="size-3.5" />
29
+ </button>
30
+ <button
31
+ onClick={() => setTheme('system')}
32
+ className={cn(
33
+ 'rounded-full p-1.5 transition-colors',
34
+ theme === 'system' ? 'bg-muted text-foreground' : 'text-muted-foreground hover:text-foreground',
35
+ )}
36
+ aria-label="System theme"
37
+ >
38
+ <Monitor className="size-3.5" />
39
+ </button>
40
+ <button
41
+ onClick={() => setTheme('dark')}
42
+ className={cn(
43
+ 'rounded-full p-1.5 transition-colors',
44
+ theme === 'dark' ? 'bg-muted text-foreground' : 'text-muted-foreground hover:text-foreground',
45
+ )}
46
+ aria-label="Dark theme"
47
+ >
48
+ <Moon className="size-3.5" />
49
+ </button>
50
+ </div>
51
+ );
52
+ }