diff --git a/apps/web/.env.example b/apps/web/.env.example index ccd113a..e2425f7 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -2,3 +2,8 @@ # Use http://localhost:3000 for local development # Use your production URL (e.g., https://your-site.vercel.app) for production NEXT_PUBLIC_APP_URL=http://localhost:3000 + +# Google Search Console verification code +# Get this from: https://search.google.com/search-console +# Format: just the code part, not the full meta tag +NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION= diff --git a/apps/web/app/docs/components/border-beam/page.tsx b/apps/web/app/docs/components/border-beam/page.tsx new file mode 100644 index 0000000..b7c708e --- /dev/null +++ b/apps/web/app/docs/components/border-beam/page.tsx @@ -0,0 +1,197 @@ +import type React from "react" +import type { Metadata } from "next" +import { BorderBeam } from "@workspace/ui/components/border-beam" +import { InstallCommand } from "@/components/install-command" +import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" + +export const metadata: Metadata = { + title: "Border Beam Component", + description: "Animated border beam effect that follows the border path. Beautiful glowing animation for React with Tailwind CSS by Harsh Jadhav.", + alternates: { + canonical: "https://componentry.fun/docs/components/border-beam", + }, +} + +const defaultBeamCode = `import { BorderBeam } from "@/components/ui/border-beam" + +
+
+

Border Beam

+

The beam follows the border path

+
+ +
` + +const customColorsCode = `import { BorderBeam } from "@/components/ui/border-beam" + +
+
+

Customized

+

Slower, larger, custom colors

+
+ +
` + +const fastBeamCode = `import { BorderBeam } from "@/components/ui/border-beam" + +
+
+

Fast Beam

+

Quick animation with warm colors

+
+ +
` + +const delayedBeamCode = `import { BorderBeam } from "@/components/ui/border-beam" + +
+
+

Delayed Start

+

Beam starts after a 3 second delay

+
+ +
` + +export default function BorderBeamPage(): React.JSX.Element { + return ( + +
+ +
+ +
+
+ +
+

Default

+
+
+

Border Beam

+

The beam follows the border path

+
+ +
+ +
+ +
+

Custom Colors

+
+
+

Customized

+

Slower, larger, custom colors

+
+ +
+ +
+ +
+

Fast Animation

+
+
+

Fast Beam

+

Quick animation with warm colors

+
+ +
+ +
+ +
+

Delayed Start

+
+
+

Delayed Start

+

Beam starts after a 3 second delay

+
+ +
+ +
+ +
+
+ +
+
+
+
size
+
+ Length of the beam in pixels (default: 200) +
+
+
+
duration
+
+ Animation duration in seconds (default: 15) +
+
+
+
borderWidth
+
+ Width of the border/beam in pixels (default: 1.5) +
+
+
+
colorFrom
+
+ Start color of the gradient (default: #ffaa40) +
+
+
+
colorTo
+
+ End color of the gradient (default: #9c40ff) +
+
+
+
delay
+
+ Animation delay in seconds (default: 0) +
+
+
+
+
+ ) +} diff --git a/apps/web/app/docs/components/button/page.tsx b/apps/web/app/docs/components/button/page.tsx deleted file mode 100644 index 79d2b92..0000000 --- a/apps/web/app/docs/components/button/page.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import type React from "react" -import { Button } from "@workspace/ui/components/button" - -export default function ButtonPage(): React.JSX.Element { - return ( -
-
-
- - 001 - -
-
-

- Button -

-

- The most fundamental interactive element. A starting point for - everything else. -

-
-
- -
-
- Variants -
-
-
- {[ - { variant: "default" as const, label: "Default" }, - { variant: "secondary" as const, label: "Secondary" }, - { variant: "outline" as const, label: "Outline" }, - { variant: "ghost" as const, label: "Ghost" }, - { variant: "destructive" as const, label: "Destructive" }, - { variant: "link" as const, label: "Link" }, - ].map(({ variant, label }) => ( -
-
- -
-

{label}

-
- ))} -
-
-
- -
-
- Scale -
-
- {[ - { size: "sm" as const, label: "S" }, - { size: "default" as const, label: "M" }, - { size: "lg" as const, label: "L" }, - ].map(({ size, label }) => ( -
- - {label} -
- ))} -
-
- -
-
- Code -
-
-
-            {`import { Button } from "@workspace/ui/components/button"
-
-
-
-`}
-          
-

- Copy-paste ready. Tweak as needed. -

-
-
-
- ) -} diff --git a/apps/web/app/docs/components/circuit-board/page.tsx b/apps/web/app/docs/components/circuit-board/page.tsx new file mode 100644 index 0000000..b4ba00d --- /dev/null +++ b/apps/web/app/docs/components/circuit-board/page.tsx @@ -0,0 +1,248 @@ +import type React from "react" +import type { Metadata } from "next" +import { CircuitBoard } from "@workspace/ui/components/circuit-board" +import { InstallCommand } from "@/components/install-command" +import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" +import { Database, Server, Cloud, Globe, Shield, GitBranch, Cpu, HardDrive, Wifi } from "lucide-react" + +export const metadata: Metadata = { + title: "Circuit Board Component", + description: "Animated circuit board visualization with nodes and connections. Perfect for tech diagrams and flow visualizations. Free React component by Harsh Jadhav.", + alternates: { + canonical: "https://componentry.fun/docs/components/circuit-board", + }, +} + +const simpleFlowCode = `import { CircuitBoard } from "@/components/ui/circuit-board" +import { Cloud, Server, Shield, Database } from "lucide-react" + + }, + { id: "process", x: 250, y: 80, label: "Server", icon: }, + { id: "validate", x: 250, y: 220, label: "Validate", icon: }, + { id: "end", x: 420, y: 150, label: "Database", icon: }, + ]} + connections={[ + { from: "start", to: "process", animated: true }, + { from: "start", to: "validate", animated: true }, + { from: "process", to: "end", animated: true }, + { from: "validate", to: "end", animated: true }, + ]} + width={500} + height={300} +/>` + +const loadBalancerCode = `import { CircuitBoard } from "@/components/ui/circuit-board" +import { Globe, GitBranch, Server, Database } from "lucide-react" + + }, + { id: "lb", x: 180, y: 150, label: "Load Balancer", icon: }, + { id: "api1", x: 300, y: 80, label: "API 1", icon: }, + { id: "api2", x: 300, y: 220, label: "API 2", icon: }, + { id: "db", x: 420, y: 150, label: "Database", icon: }, + ]} + connections={[ + { from: "user", to: "lb", animated: true }, + { from: "lb", to: "api1", animated: true }, + { from: "lb", to: "api2", animated: true }, + { from: "api1", to: "db", animated: true }, + { from: "api2", to: "db", animated: true }, + ]} + width={480} + height={300} +/>` + +const bidirectionalCode = `import { CircuitBoard } from "@/components/ui/circuit-board" +import { Cpu, HardDrive, Database } from "lucide-react" + + }, + { id: "ram", x: 250, y: 100, label: "RAM", icon: }, + { id: "storage", x: 400, y: 100, label: "Storage", icon: }, + ]} + connections={[ + { from: "cpu", to: "ram", bidirectional: true }, + { from: "ram", to: "storage", bidirectional: true }, + ]} + width={500} + height={200} + showGrid={false} +/>` + +const networkCode = `import { CircuitBoard } from "@/components/ui/circuit-board" +import { Wifi, Server, Globe } from "lucide-react" + + }, + { id: "server1", x: 100, y: 220, label: "Server 1", icon: }, + { id: "server2", x: 240, y: 220, label: "Server 2", icon: }, + { id: "server3", x: 380, y: 220, label: "Server 3", icon: }, + ]} + connections={[ + { from: "router", to: "server1", animated: true }, + { from: "router", to: "server2", animated: true }, + { from: "router", to: "server3", animated: true }, + ]} + width={480} + height={300} + pulseSpeed={1.5} +/>` + +export default function CircuitBoardPage(): React.JSX.Element { + return ( + +
+ +
+ +
+
+ +
+

Simple Flow

+
+ }, + { id: "process", x: 250, y: 80, label: "Server", icon: }, + { id: "validate", x: 250, y: 220, label: "Validate", icon: }, + { id: "end", x: 420, y: 150, label: "Database", icon: }, + ]} + connections={[ + { from: "start", to: "process", animated: true }, + { from: "start", to: "validate", animated: true }, + { from: "process", to: "end", animated: true }, + { from: "validate", to: "end", animated: true }, + ]} + width={500} + height={300} + /> +
+ +
+ +
+

Load Balancer

+
+ }, + { id: "lb", x: 180, y: 150, label: "Load Balancer", icon: }, + { id: "api1", x: 300, y: 80, label: "API 1", icon: }, + { id: "api2", x: 300, y: 220, label: "API 2", icon: }, + { id: "db", x: 420, y: 150, label: "Database", icon: }, + ]} + connections={[ + { from: "user", to: "lb", animated: true }, + { from: "lb", to: "api1", animated: true }, + { from: "lb", to: "api2", animated: true }, + { from: "api1", to: "db", animated: true }, + { from: "api2", to: "db", animated: true }, + ]} + width={480} + height={300} + /> +
+ +
+ +
+

Bidirectional

+
+ }, + { id: "ram", x: 250, y: 100, label: "RAM", icon: }, + { id: "storage", x: 400, y: 100, label: "Storage", icon: }, + ]} + connections={[ + { from: "cpu", to: "ram", bidirectional: true }, + { from: "ram", to: "storage", bidirectional: true }, + ]} + width={500} + height={200} + showGrid={false} + /> +
+ +
+ +
+

Network Topology

+
+ }, + { id: "server1", x: 100, y: 220, label: "Server 1", icon: }, + { id: "server2", x: 240, y: 220, label: "Server 2", icon: }, + { id: "server3", x: 380, y: 220, label: "Server 3", icon: }, + ]} + connections={[ + { from: "router", to: "server1", animated: true }, + { from: "router", to: "server2", animated: true }, + { from: "router", to: "server3", animated: true }, + ]} + width={480} + height={300} + pulseSpeed={1.5} + /> +
+ +
+ +
+
+ +
+
+
+
nodes
+
+ Array of node objects with id, x, y, label, size, and icon +
+
+
+
connections
+
+ Array of connection objects with from, to, animated, bidirectional +
+
+
+
width / height
+
+ Dimensions of the circuit board (default: 600 x 400) +
+
+
+
showGrid
+
+ Show dot grid background (default: true) +
+
+
+
pulseSpeed
+
+ Speed of the electricity animation in seconds (default: 2) +
+
+
+
traceWidth
+
+ Width of the connection traces in pixels (default: 2) +
+
+
+
+
+ ) +} diff --git a/apps/web/app/docs/components/command-menu/demo.tsx b/apps/web/app/docs/components/command-menu/demo.tsx new file mode 100644 index 0000000..763be6b --- /dev/null +++ b/apps/web/app/docs/components/command-menu/demo.tsx @@ -0,0 +1,55 @@ +"use client" + +import { CommandMenu } from "@workspace/ui/components/command-menu" +import { FileText, Settings, User, Hash, Home, BookOpen } from "lucide-react" + +const demoGroups = [ + { + title: "Pages", + items: [ + { id: "home", title: "Home", icon: , onSelect: () => console.log("Home") }, + { id: "about", title: "About", icon: , onSelect: () => console.log("About") }, + { id: "docs", title: "Documentation", icon: , onSelect: () => console.log("Docs") }, + ], + }, + { + title: "Settings", + items: [ + { id: "profile", title: "Profile", icon: , onSelect: () => console.log("Profile") }, + { id: "settings", title: "Settings", icon: , onSelect: () => console.log("Settings") }, + ], + }, +] + +export function CommandMenuDemo() { + return ( + + ) +} + +const customGroups = [ + { + title: "Documentation", + items: [ + { id: "intro", title: "Introduction", icon: , onSelect: () => console.log("Intro") }, + { id: "api", title: "API Reference", icon: , onSelect: () => console.log("API") }, + { id: "examples", title: "Examples", icon: , onSelect: () => console.log("Examples") }, + ], + }, +] + +export function CommandMenuCustomDemo() { + return ( + + ) +} diff --git a/apps/web/app/docs/components/command-menu/page.tsx b/apps/web/app/docs/components/command-menu/page.tsx new file mode 100644 index 0000000..9e12660 --- /dev/null +++ b/apps/web/app/docs/components/command-menu/page.tsx @@ -0,0 +1,211 @@ +import type React from "react" +import type { Metadata } from "next" +import { InstallCommand } from "@/components/install-command" +import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" +import { CommandMenuDemo, CommandMenuCustomDemo } from "./demo" + +export const metadata: Metadata = { + title: "Command Menu Component", + description: "A macOS Spotlight-style command menu with animated search, keyboard navigation, and customizable groups. Features backdrop blur, smooth animations, and full keyboard support.", + alternates: { + canonical: "https://componentry.fun/docs/components/command-menu", + }, +} + +const basicUsageCode = `import { CommandMenu } from "@/components/ui/command-menu" +import { FileText, Settings, User, Search } from "lucide-react" + +const groups = [ + { + title: "Pages", + items: [ + { id: "home", title: "Home", icon: , onSelect: () => console.log("Home") }, + { id: "about", title: "About", icon: , onSelect: () => console.log("About") }, + ], + }, + { + title: "Settings", + items: [ + { id: "profile", title: "Profile", icon: , onSelect: () => console.log("Profile") }, + { id: "settings", title: "Settings", icon: , onSelect: () => console.log("Settings") }, + ], + }, +] + +` + +const customShortcutCode = `import { CommandMenu } from "@/components/ui/command-menu" +import { FileText, Hash } from "lucide-react" + +const groups = [ + { + title: "Documentation", + items: [ + { id: "intro", title: "Introduction", icon: , onSelect: () => {} }, + { id: "api", title: "API Reference", icon: , onSelect: () => {} }, + ], + }, +] + +` + +const controlledCode = `import { useState } from "react" +import { CommandMenu } from "@/components/ui/command-menu" + +function MyComponent() { + const [open, setOpen] = useState(false) + + return ( + <> + + + + ) +}` + +export default function CommandMenuPage(): React.JSX.Element { + return ( + +
+ +
+ +
+
+ +
+

Default

+
+ +
+ +
+ +
+

Custom Shortcut & Labels

+
+ +
+ +
+ +
+

Controlled State

+ +

+ Use the open and onOpenChange props to control the menu state externally. +

+
+ +
+
+ +
+
+
+
groups
+
+ Array of menu groups with title and items. Each item has id, title, icon (optional), and onSelect callback. +
+
+
+
placeholder
+
+ Placeholder text for the search input (default: "Search...") +
+
+
+
emptyMessage
+
+ Message shown when no results are found (default: "No results found") +
+
+
+
brandName
+
+ Brand name displayed in the footer (default: "Command Menu") +
+
+
+
triggerLabel
+
+ Label for the trigger button (default: "Search...") +
+
+
+
triggerClassName
+
+ Additional CSS classes for the trigger button +
+
+
+
shortcutKey
+
+ Keyboard shortcut key used with Cmd/Ctrl (default: "K") +
+
+
+
open
+
+ Controlled open state (optional) +
+
+
+
onOpenChange
+
+ Callback when open state changes (optional) +
+
+
+
+ +
+
+
+
⌘/Ctrl + K
+
+ Toggle the command menu (customizable via shortcutKey prop) +
+
+
+
↑ / ↓
+
+ Navigate through menu items +
+
+
+
Enter
+
+ Select the highlighted item +
+
+
+
Escape
+
+ Close the command menu +
+
+
+
+
+ ) +} diff --git a/apps/web/app/docs/components/dither-gradient/page.tsx b/apps/web/app/docs/components/dither-gradient/page.tsx new file mode 100644 index 0000000..d165433 --- /dev/null +++ b/apps/web/app/docs/components/dither-gradient/page.tsx @@ -0,0 +1,157 @@ +import type React from "react" +import type { Metadata } from "next" +import { DitherGradient } from "@workspace/ui/components/dither-gradient" +import { InstallCommand } from "@/components/install-command" +import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" + +export const metadata: Metadata = { + title: "Dither Gradient Component", + description: "Animated dithered gradient background effect using canvas with Bayer matrix dithering. Beautiful retro-style gradients for React with Tailwind CSS by Harsh Jadhav.", + alternates: { + canonical: "https://componentry.fun/docs/components/dither-gradient", + }, +} + +const defaultCode = `import { DitherGradient } from "@/components/ui/dither-gradient" + +
+ +
+

Dither Gradient

+
+
` + +const customColorsCode = `import { DitherGradient } from "@/components/ui/dither-gradient" + +
+ +
+

Ocean to Forest

+
+
` + +const highIntensityCode = `import { DitherGradient } from "@/components/ui/dither-gradient" + +
+ +
+

Sunset Fire

+
+
` + +export default function DitherGradientPage(): React.JSX.Element { + return ( + +
+ +
+ +
+
+ +
+

Default

+
+ +
+

Dither Gradient

+
+
+ +
+ +
+

Ocean to Forest

+
+ +
+

Ocean to Forest

+
+
+ +
+ +
+

Sunset Fire

+
+ +
+

Sunset Fire

+
+
+ +
+ +
+
+ +
+
+
+
colorFrom
+
+ Start color of the gradient (default: #6366f1) +
+
+
+
colorMid
+
+ Middle color of the gradient (default: #8b5cf6) +
+
+
+
colorTo
+
+ End color of the gradient (default: #ec4899) +
+
+
+
intensity
+
+ Dithering intensity from 0 to 1 (default: 0.15) +
+
+
+
speed
+
+ Animation speed multiplier (default: 3) +
+
+
+
angle
+
+ Gradient angle in degrees (default: 45) +
+
+
+
+
+ ) +} diff --git a/apps/web/app/docs/components/flight-status-card/page.tsx b/apps/web/app/docs/components/flight-status-card/page.tsx index 69844aa..4f72c27 100644 --- a/apps/web/app/docs/components/flight-status-card/page.tsx +++ b/apps/web/app/docs/components/flight-status-card/page.tsx @@ -1,14 +1,24 @@ import type React from "react" +import type { Metadata } from "next" import { FlightStatusCardAdaptive } from "@workspace/ui/components/flight-status-card" import { InstallCommand } from "@/components/install-command" import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" -const usageCode = `import { FlightStatusCardAdaptive } from "components/ui/flight-status-card" +export const metadata: Metadata = { + title: "Flight Status Card Component", + description: "A detailed flight status widget with dot-matrix airport codes, progress tracking, and ETA. Free React component with Tailwind CSS by Harsh Jadhav.", + alternates: { + canonical: "https://componentry.fun/docs/components/flight-status-card", + }, +} + +const defaultCardCode = `import { FlightStatusCardAdaptive } from "@/components/ui/flight-status-card" + +` -// Auto-switches between dark and light mode - +const customRouteCode = `import { FlightStatusCardAdaptive } from "@/components/ui/flight-status-card" -// With custom props -
-
- - 002 - -
-
-

- Flight Status Card -

-

- A detailed flight status widget with dot-matrix airport codes, - progress tracking, and ETA information. Pixel-perfect recreation of - a premium travel app design. -

-
-
+ -
- Preview -
-
-
- -
-

- Automatically adapts to light and dark mode. Toggle the theme to see it in action. -

-
- + > +
+ +
-
-
- Custom -
-
-
- +
+
+ +
+

Default

+
+ +
+
-

- Fully customizable with any route, times, and progress state. -

-
-
-
-
- Install -
-
- -

- Requires shadcn CLI. Run npx shadcn@latest init first if not set up. -

-
-
+
+

Custom Route

+
+ +
+ +
-
-
- Code -
-
- -

- Import and use directly. All props are optional with sensible - defaults. -

-
+ -
-
- Features -
-
-
-
-
Dot Matrix Display
-
- LED-style airport code rendering with individual dot animations -
+
+
+
+
Dot Matrix Display
+
+ LED-style airport code rendering with individual dot animations
-
-
Halftone Pattern
-
- Subtle gradient dot pattern for visual depth -
+
+
+
Halftone Pattern
+
+ Subtle gradient dot pattern for visual depth
-
-
Progress Tracking
-
- Animated progress bar with glowing plane icon -
+
+
+
Progress Tracking
+
+ Animated progress bar with glowing plane icon
-
-
ETA Panel
-
- Real-time arrival estimates with timezone and event countdown -
+
+
+
ETA Panel
+
+ Real-time arrival estimates with timezone and event countdown
-
-
Theme Support
-
- Separate dark and light mode variants with adaptive option -
+
+
+
Theme Support
+
+ Separate dark and light mode variants with adaptive option
-
-
+
+ ) } diff --git a/apps/web/app/docs/components/liquid-blob/page.tsx b/apps/web/app/docs/components/liquid-blob/page.tsx new file mode 100644 index 0000000..d6406e8 --- /dev/null +++ b/apps/web/app/docs/components/liquid-blob/page.tsx @@ -0,0 +1,166 @@ +import type React from "react" +import type { Metadata } from "next" +import { LiquidBlob } from "@workspace/ui/components/liquid-blob" +import { InstallCommand } from "@/components/install-command" +import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" + +export const metadata: Metadata = { + title: "Liquid Blob Component", + description: "Animated liquid morphing blob shapes that create a beautiful organic background effect. Glassmorphism style blobs for React with Tailwind CSS by Harsh Jadhav.", + alternates: { + canonical: "https://componentry.fun/docs/components/liquid-blob", + }, +} + +const defaultCode = `import { LiquidBlob } from "@/components/ui/liquid-blob" + +// Hover over the container to see the interactive effect! +
+ +
+

Liquid Blob

+
+
` + +const customColorsCode = `import { LiquidBlob } from "@/components/ui/liquid-blob" + +
+ +
+

Teal Theme

+
+
` + +const fastBlobCode = `import { LiquidBlob } from "@/components/ui/liquid-blob" + +
+ +
+

Fast Animation

+
+
` + +export default function LiquidBlobPage(): React.JSX.Element { + return ( + +
+ +
+ +
+
+ +
+

Interactive (Hover Me!)

+
+ +
+

Liquid Blob

+
+
+ +
+ +
+

Teal Theme

+
+ +
+

Teal Theme

+
+
+ +
+ +
+

Fast Animation

+
+ +
+

Fast Animation

+
+
+ +
+ +
+
+ +
+
+
+
color
+
+ Primary blob color (default: #8b5cf6) +
+
+
+
secondaryColor
+
+ Secondary blob color (default: #ec4899) +
+
+
+
size
+
+ Base size of the blobs in pixels (default: 300) +
+
+
+
blur
+
+ Blur amount for the soft glow effect (default: 60) +
+
+
+
speed
+
+ Animation duration in seconds (default: 8) +
+
+
+
opacity
+
+ Blob opacity from 0 to 1 (default: 0.7) +
+
+
+
interactive
+
+ Enable mouse hover interaction (default: true) +
+
+
+
+
+ ) +} diff --git a/apps/web/app/docs/components/magnetic-dock/page.tsx b/apps/web/app/docs/components/magnetic-dock/page.tsx new file mode 100644 index 0000000..383f9de --- /dev/null +++ b/apps/web/app/docs/components/magnetic-dock/page.tsx @@ -0,0 +1,288 @@ +import type React from "react" +import type { Metadata } from "next" +import { + MagneticDock, + DockIconHome, + DockIconSearch, + DockIconFolder, + DockIconMail, + DockIconMusic, + DockIconSettings, + DockIconTrash, +} from "@workspace/ui/components/magnetic-dock" +import { InstallCommand } from "@/components/install-command" +import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" + +export const metadata: Metadata = { + title: "Magnetic Dock Component", + description: "A macOS-style magnetic dock with smooth scaling animations, spring physics, tooltips, badges, and premium micro-interactions. Free React component by Harsh Jadhav.", + alternates: { + canonical: "https://componentry.fun/docs/components/magnetic-dock", + }, +} + +const basicCode = `import { + MagneticDock, + DockIconHome, + DockIconSearch, + DockIconFolder, + DockIconMail, + DockIconMusic, + DockIconSettings, + DockIconTrash, +} from "@/components/ui/magnetic-dock" + +const items = [ + { id: "home", label: "Home", icon: , isActive: true }, + { id: "search", label: "Search", icon: }, + { id: "folder", label: "Finder", icon: }, + { id: "mail", label: "Mail", icon: , badge: 3 }, + { id: "music", label: "Music", icon: }, + { id: "settings", label: "Settings", icon: }, + { id: "trash", label: "Trash", icon: }, +] + +` + +const solidCode = `` + +const customScaleCode = `` + +const defaultItems = [ + { id: "home", label: "Home", icon: , isActive: true }, + { id: "search", label: "Search", icon: }, + { id: "folder", label: "Finder", icon: }, + { id: "mail", label: "Mail", icon: , badge: 3 }, + { id: "music", label: "Music", icon: }, + { id: "settings", label: "Settings", icon: }, + { id: "trash", label: "Trash", icon: }, +] + +const minimalItems = [ + { id: "home", label: "Home", icon: }, + { id: "search", label: "Search", icon: }, + { id: "folder", label: "Finder", icon: }, + { id: "settings", label: "Settings", icon: }, +] + +export default function MagneticDockPage(): React.JSX.Element { + return ( + +
+ +
+ +
+
+ +
+

Default (Glass)

+
+ +
+ +
+ +
+

Solid Variant

+
+ +
+ +
+ +
+

Large Scale Effect

+
+ +
+ +
+ +
+
+ +
+
+
+
Magnetic Scaling
+
+ Icons smoothly scale based on cursor proximity using spring physics +
+
+
+
Spring Physics
+
+ Framer Motion springs create natural, fluid animations with proper physics +
+
+
+
Floating Effect
+
+ Icons rise up when magnified, creating a 3D lifting effect +
+
+
+
Tooltips
+
+ Animated tooltips appear on hover with smooth enter/exit transitions +
+
+
+
Badge Support
+
+ Notification badges with animated appearance and 99+ overflow handling +
+
+
+
Active States
+
+ Visual indicator dot for currently active items +
+
+
+
Glass Morphism
+
+ Frosted glass effect with backdrop blur and subtle reflections +
+
+
+
+ +
+
+
+
items
+
+ Array of dock items with id, label, icon, onClick, isActive, and badge (required) +
+
+
+
iconSize
+
+ Base size of icons in pixels (default: 56) +
+
+
+
maxScale
+
+ Maximum scale factor when hovering directly over an icon (default: 1.5) +
+
+
+
magneticDistance
+
+ Pixel distance for magnetic effect falloff (default: 150) +
+
+
+
showLabels
+
+ Show tooltip labels on hover (default: true) +
+
+
+
position
+
+ Dock orientation: "bottom", "top", "left", or "right" (default: "bottom") +
+
+
+
variant
+
+ Background style: "glass", "solid", or "transparent" (default: "glass") +
+
+
+
+ +
+
+
+
id
+
+ Unique identifier for the item (required) +
+
+
+
label
+
+ Display label shown in tooltip (required) +
+
+
+
icon
+
+ React node for the icon (required) +
+
+
+
onClick
+
+ Click handler function +
+
+
+
isActive
+
+ Whether the item is currently active (shows indicator dot) +
+
+
+
badge
+
+ Notification badge count (displays 99+ for values over 99) +
+
+
+
+ +
+
+
+
DockIconHome
+
Home/house icon
+
+
+
DockIconSearch
+
Magnifying glass search icon
+
+
+
DockIconFolder
+
Folder/finder icon
+
+
+
DockIconMail
+
Envelope mail icon
+
+
+
DockIconMusic
+
Music note icon
+
+
+
DockIconSettings
+
Gear/cog settings icon
+
+
+
DockIconTrash
+
Trash/delete icon
+
+
+
+
+ ) +} diff --git a/apps/web/app/docs/components/noise-texture/page.tsx b/apps/web/app/docs/components/noise-texture/page.tsx new file mode 100644 index 0000000..e5c4694 --- /dev/null +++ b/apps/web/app/docs/components/noise-texture/page.tsx @@ -0,0 +1,143 @@ +import type React from "react" +import type { Metadata } from "next" +import { NoiseTexture } from "@workspace/ui/components/noise-texture" +import { InstallCommand } from "@/components/install-command" +import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" + +export const metadata: Metadata = { + title: "Noise Texture Component", + description: "Animated noise/grain texture overlay effect with customizable grain size and blend modes. Film grain effect for React with Tailwind CSS by Harsh Jadhav.", + alternates: { + canonical: "https://componentry.fun/docs/components/noise-texture", + }, +} + +const defaultCode = `import { NoiseTexture } from "@/components/ui/noise-texture" + +
+ +
+

Noise Texture

+
+
` + +const coarseGrainCode = `import { NoiseTexture } from "@/components/ui/noise-texture" + +
+ +
+

Coarse Grain

+
+
` + +const staticNoiseCode = `import { NoiseTexture } from "@/components/ui/noise-texture" + +
+ +
+

Static Noise

+
+
` + +export default function NoiseTexturePage(): React.JSX.Element { + return ( + +
+ +
+ +
+
+ +
+

Default

+
+ +
+

Noise Texture

+
+
+ +
+ +
+

Coarse Grain

+
+ +
+

Coarse Grain

+
+
+ +
+ +
+

Static Noise

+
+ +
+

Static Noise

+
+
+ +
+ +
+
+ +
+
+
+
opacity
+
+ Noise overlay opacity from 0 to 1 (default: 0.15) +
+
+
+
speed
+
+ Animation frames per second (default: 10) +
+
+
+
grain
+
+ Grain size: "fine" | "medium" | "coarse" (default: "medium") +
+
+
+
blend
+
+ CSS blend mode: "overlay" | "soft-light" | "multiply" | "screen" (default: "overlay") +
+
+
+
animate
+
+ Enable/disable animation (default: true) +
+
+
+
+
+ ) +} diff --git a/apps/web/app/docs/components/showcase-card/page.tsx b/apps/web/app/docs/components/showcase-card/page.tsx new file mode 100644 index 0000000..cd1a2ea --- /dev/null +++ b/apps/web/app/docs/components/showcase-card/page.tsx @@ -0,0 +1,274 @@ +import type React from "react" +import type { Metadata } from "next" +import { + ShowcaseCard, + ShowcaseCardCompact, + ShowcaseGrid, +} from "@workspace/ui/components/showcase-card" +import { InstallCommand } from "@/components/install-command" +import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" + +export const metadata: Metadata = { + title: "Showcase Card Component", + description: "A premium showcase card with 3D tilt effect, parallax image, and micro-interactions. Perfect for portfolios and agency sites. Free React component by Harsh Jadhav.", + alternates: { + canonical: "https://componentry.fun/docs/components/showcase-card", + }, +} + +const defaultCardCode = `import { ShowcaseCard } from "@/components/ui/showcase-card" + + console.log("CTA clicked")} + brandName="studio.design" + services={["web", "product", "brand"]} +/>` + +const noTiltCode = `import { ShowcaseCard } from "@/components/ui/showcase-card" + +` + +const compactCode = `import { ShowcaseCardCompact, ShowcaseGrid } from "@/components/ui/showcase-card" + + + + + +` + +export default function ShowcaseCardPage(): React.JSX.Element { + return ( + +
+ +
+ +
+
+ +
+

Default

+
+ +
+ +
+ +
+

Without 3D Effects

+
+ +
+ +
+ +
+

Compact Grid

+
+ + + + + +
+ +
+ +
+
+ +
+
+
+
3D Tilt Effect
+
+ Smooth perspective-based tilt that follows cursor movement with spring physics +
+
+
+
Image Parallax
+
+ Hero image moves subtly in response to cursor position for depth +
+
+
+
Glow Effects
+
+ Dynamic radial gradient glow follows cursor for premium feel +
+
+
+
Button Shine
+
+ CTA button features a sweeping shine animation on hover +
+
+
+
Multiple Variants
+
+ Choose from default, compact (for grids), or horizontal layouts +
+
+
+
Fully Responsive
+
+ Adapts beautifully from mobile to desktop with optimized touch interactions +
+
+
+
+ +
+
+
+
tagline
+
+ Optional text displayed at the top of the image section +
+
+
+
heading
+
+ Main title text (required) +
+
+
+
description
+
+ Supporting text below the heading +
+
+
+
imageUrl
+
+ URL for the hero image (required) +
+
+
+
ctaText
+
+ Text for the call-to-action button +
+
+
+
onCtaClick
+
+ Click handler for the CTA button +
+
+
+
brandName
+
+ Brand or company name for the footer +
+
+
+
services
+
+ Array of service tags displayed in footer (e.g., ["web", "product"]) +
+
+
+
enableTilt
+
+ Enable 3D tilt effect on hover (default: true) +
+
+
+
maxTilt
+
+ Maximum tilt angle in degrees (default: 8) +
+
+
+
enableParallax
+
+ Enable parallax effect on image (default: true) +
+
+
+
+ +
+
+
+
ShowcaseCard
+
+ Default vertical card with full features, 3D tilt, and parallax +
+
+
+
ShowcaseCardCompact
+
+ Minimal card for grids, with hover scale and arrow indicator +
+
+
+
ShowcaseGrid
+
+ Responsive grid container for ShowcaseCardCompact items +
+
+
+
+
+ ) +} diff --git a/apps/web/app/docs/components/spotlight-card/page.tsx b/apps/web/app/docs/components/spotlight-card/page.tsx index 23d9aa0..81ba3e7 100644 --- a/apps/web/app/docs/components/spotlight-card/page.tsx +++ b/apps/web/app/docs/components/spotlight-card/page.tsx @@ -1,4 +1,5 @@ import type React from "react" +import type { Metadata } from "next" import { SpotlightCard, SpotlightCardContent, @@ -12,49 +13,97 @@ import { } from "@workspace/ui/components/spotlight-card" import { InstallCommand } from "@/components/install-command" import { CodeBlock } from "@/components/code-block" +import { ComponentLayout, Section } from "@/components/component-layout" -const usageCode = `import { +export const metadata: Metadata = { + title: "Spotlight Card Component", + description: "Interactive spotlight card with cursor tracking effects, tilt animations, and gradient highlights. Free React component by Harsh Jadhav.", + alternates: { + canonical: "https://componentry.fun/docs/components/spotlight-card", + }, +} + +const defaultSpotlightCode = `import { SpotlightCard, SpotlightCardContent, SpotlightCardHeader, SpotlightCardTitle, SpotlightCardDescription, -} from "components/ui/spotlight-card" +} from "@/components/ui/spotlight-card" - Spotlight Card + Spotlight Effect - Hover over this card to see the spotlight effect + Hover over this card to see the spotlight follow your cursor - Your content here +

+ This card features an animated gradient border and a soft + spotlight glow that tracks your mouse movement. +

` -const multiSpotlightCode = ` +const customSpotlightCode = `import { + SpotlightCard, + SpotlightCardContent, + SpotlightCardHeader, + SpotlightCardTitle, + SpotlightCardDescription, +} from "@/components/ui/spotlight-card" + + + + Custom Colors + + Fully customizable spotlight color and intensity + + + +

+ Customize the spotlight color, glow intensity, border radius, + and more to match your design system. +

+
+
` + +const multiSpotlightCode = `import { MultiSpotlightCard } from "@/components/ui/spotlight-card" + +

Multi Spotlight

Multiple colored spotlight sources follow your cursor

` -const beamSpotlightCode = ` +const beamSpotlightCode = `import { BeamSpotlightCard } from "@/components/ui/spotlight-card" + +

Beam Spotlight

Crossing light beams create a dramatic effect

` -const gradientFollowCode = ` +const gradientFollowCode = `import { GradientFollowCard } from "@/components/ui/spotlight-card" + +

Gradient Follow

Dynamic gradient background follows cursor position

` -const tiltSpotlightCode = ` +const tiltSpotlightCode = `import { TiltSpotlightCard } from "@/components/ui/spotlight-card" + +

3D Tilt

Perspective tilt with glare effect for depth @@ -63,129 +112,71 @@ const tiltSpotlightCode = ` -

-
- - 003 - -
-
-

- Spotlight Card -

-

- Interactive cards with cursor-following spotlight effects, animated - gradient borders, and 3D tilt animations. Inspired by the premium - designs of Vercel, Linear, and Stripe. -

-
-
- -
-
- Preview -
-
-
- - - Spotlight Effect - - Hover over this card to see the spotlight follow your cursor - - - -

- This card features an animated gradient border and a soft - spotlight glow that tracks your mouse movement for an - interactive experience. -

-
-
-
-

- Move your cursor over the card to see the spotlight effect in - action. -

-
-
+ +
+ +
+
+
-
-
- Custom -
-
-
- - - Custom Colors - - Fully customizable spotlight color and intensity - - - -

- Customize the spotlight color, glow intensity, border radius, - and more to match your design system. -

-
-
+
+

Default

+
+ + + Spotlight Effect + + Hover over this card to see the spotlight follow your cursor + + + +

+ This card features an animated gradient border and a soft + spotlight glow that tracks your mouse movement. +

+
+
+
+
-

- All props are customizable to match your brand colors. -

-
-
-
-
- Install -
-
- -

- Requires shadcn CLI. Run{" "} - npx shadcn@latest init{" "} - first if not set up. -

-
-
- -
-
- Code -
-
- -

- Basic usage with the structured card components. -

-
-
+
+

Custom Colors

+
+ + + Custom Colors + + Fully customizable spotlight color and intensity + + + +

+ Customize the spotlight color, glow intensity, border radius, + and more to match your design system. +

+
+
+
+ +
-
-
- Examples -
-
-

Multi Spotlight

-
+
-

Multi Spotlight

-

+

Multi Spotlight

+

Multiple colored spotlight sources follow your cursor

@@ -195,10 +186,10 @@ export default function SpotlightCardPage(): React.JSX.Element {

Beam Spotlight

-
+
-

Beam Spotlight

-

+

Beam Spotlight

+

Crossing light beams create a dramatic effect

@@ -208,10 +199,10 @@ export default function SpotlightCardPage(): React.JSX.Element {

Gradient Follow

-
+
-

Gradient Follow

-

+

Gradient Follow

+

Dynamic gradient background follows cursor position

@@ -221,10 +212,10 @@ export default function SpotlightCardPage(): React.JSX.Element {

3D Tilt

-
+
-

3D Tilt

-

+

3D Tilt

+

Perspective tilt with glare effect for depth

@@ -233,89 +224,79 @@ export default function SpotlightCardPage(): React.JSX.Element {
-
+
-
-
- Features -
-
-
-
-
Cursor Tracking
-
- Spotlight effect smoothly follows mouse position across the card -
+
+
+
+
Cursor Tracking
+
+ Spotlight effect smoothly follows mouse position across the card
-
-
Animated Border
-
- Rotating gradient border creates a subtle animated glow -
+
+
+
Animated Border
+
+ Rotating gradient border creates a subtle animated glow
-
-
3D Tilt Effect
-
- Perspective-based tilt with glare for realistic depth -
+
+
+
3D Tilt Effect
+
+ Perspective-based tilt with glare for realistic depth
-
-
Multiple Variants
-
- Choose from spotlight, multi-spotlight, beam, gradient, or tilt -
+
+
+
Multiple Variants
+
+ Choose from spotlight, multi-spotlight, beam, gradient, or tilt
-
-
Customizable
-
- Fully configurable colors, intensity, radius, and animation -
+
+
+
Customizable
+
+ Fully configurable colors, intensity, radius, and animation
-
-
Performance
-
- GPU-accelerated animations with smooth 60fps transitions -
+
+
+
Performance
+
+ GPU-accelerated animations with smooth 60fps transitions
-
+
-
-
- Props -
-
-
-
-
spotlightColor
-
- Color of the spotlight effect (default: rgba(120, 119, 198, - 0.3)) -
+
+
+
+
spotlightColor
+
+ Color of the spotlight effect (default: rgba(120, 119, 198, + 0.3))
-
-
glowIntensity
-
- Intensity of the glow effect 0-1 (default: 0.15) -
+
+
+
glowIntensity
+
+ Intensity of the glow effect 0-1 (default: 0.15)
-
-
borderRadius
-
- Border radius in pixels (default: 16) -
+
+
+
borderRadius
+
+ Border radius in pixels (default: 16)
-
-
maxTilt
-
- Maximum tilt angle in degrees for TiltSpotlightCard (default: - 10) -
+
+
+
maxTilt
+
+ Maximum tilt angle in degrees for TiltSpotlightCard (default: + 10)
-
-
+
+ ) } diff --git a/apps/web/app/docs/layout.tsx b/apps/web/app/docs/layout.tsx index ce4681c..25e25df 100644 --- a/apps/web/app/docs/layout.tsx +++ b/apps/web/app/docs/layout.tsx @@ -1,4 +1,5 @@ import type React from "react" +import type { Metadata } from "next" import Link from "next/link" import { ThemeToggle } from "@/components/theme-toggle" import { CommandMenu } from "@/components/command-menu" @@ -6,6 +7,18 @@ import { DocsSidebar } from "@/components/docs-sidebar" import { TableOfContents } from "@/components/table-of-contents" import { Logomark } from "@/components/logos/logomark" +export const metadata: Metadata = { + title: "Components Documentation", + description: "Browse the complete collection of Componentry UI components. Free, open-source React components with copy-paste code, Tailwind CSS styling, and Framer Motion animations by Harsh Jadhav.", + openGraph: { + title: "UI Components Documentation | Componentry by Harsh Jadhav", + description: "Browse all React UI components. Copy-paste ready code with Tailwind CSS and Framer Motion.", + }, + alternates: { + canonical: "https://componentry.fun/docs", + }, +} + export default function DocsLayout({ children, }: { @@ -33,20 +46,22 @@ export default function DocsLayout({
-
+
diff --git a/apps/web/app/docs/page.tsx b/apps/web/app/docs/page.tsx index 4b673a2..4b59992 100644 --- a/apps/web/app/docs/page.tsx +++ b/apps/web/app/docs/page.tsx @@ -1,9 +1,50 @@ +"use client" + import type React from "react" import Link from "next/link" +import { motion } from "framer-motion" +import { ArrowRight, Box, Component, Zap, Layout, ArrowUpRight } from "lucide-react" + +const container = { + hidden: { opacity: 0 }, + show: { + opacity: 1, + transition: { + staggerChildren: 0.1 + } + } +} + +const item = { + hidden: { opacity: 0, y: 20 }, + show: { opacity: 1, y: 0 } +} + +function BentoCard({ + children, + className = "", + href +}: { + children: React.ReactNode + className?: string + href: string +}) { + return ( + +
+ {children} +
+
+ +
+ + ) +} export default function DocsIntroPage(): React.JSX.Element { return ( -
+
+ {/* Manifesto Section */}

Manifesto @@ -23,46 +64,96 @@ export default function DocsIntroPage(): React.JSX.Element {

-
-
-

- “Progress over polish.” -

-
+ + {/* Main Feature Card - Flight Status */} + + +
+
+
+ + Featured Component +
+

Flight Status Card

+

+ A complex, real-world data visualization component with dot-matrix typography and smooth animations. +

+
+
+ {/* Abstract visual representation */} +
+
+
+
+
+
+
+
+
+
+ + -
-
-

Values

-
    -
  • Clarity
  • -
  • Experimentation
  • -
  • Learning
  • -
-
-
-

Not

-
    -
  • A design system
  • -
  • A UI kit
  • -
  • Stable
  • -
-
-
+ {/* Button Card */} + + +
+
+ +
+
+

Buttons

+

Interactive variants

+
+
+
+
-
- - 001 - Button - - -
-
+ {/* Spotlight Card */} + + +
+
+ +
+
+

Spotlight

+

Cursor tracking effects

+
+
+
+
+ + {/* Shimmer Button Card */} + + +
+
+ +
+
+

Shimmer

+

Loading & highlight states

+
+
+
+
+ + {/* Coming Soon / More */} + +
+ More components in the works... + + Request a component + +
+
+
) } diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index dca8450..fd4f01f 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,9 +1,10 @@ import type React from "react" -import type { Metadata } from "next" +import type { Metadata, Viewport } from "next" import { Inter, Instrument_Serif, Syne } from "next/font/google" - +import { Analytics } from "@vercel/analytics/next" import "@workspace/ui/globals.css" import { Providers } from "@/components/providers" +import { JsonLd } from "@/components/seo/json-ld" const fontSans = Inter({ subsets: ["latin"], @@ -21,35 +22,128 @@ const fontDisplay = Syne({ variable: "--font-display", }) +const siteUrl = "https://www.componentry.fun" + +export const viewport: Viewport = { + width: "device-width", + initialScale: 1, + maximumScale: 5, + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "#ffffff" }, + { media: "(prefers-color-scheme: dark)", color: "#000000" }, + ], +} + export const metadata: Metadata = { - title: "Component Playground", - description: "A personal workshop for handcrafted UI components.", - metadataBase: new URL(process.env.NEXT_PUBLIC_APP_URL || ""), + metadataBase: new URL(siteUrl), + title: { + default: "Componentry - Premium React UI Component Library by Harsh Jadhav", + template: "%s | Componentry - UI Component Library", + }, + description: "Componentry is a free, open-source React UI component library by Harsh Jadhav. Beautiful, animated, copy-paste components built with Tailwind CSS, TypeScript, and Framer Motion. The best UI components for modern web applications.", + keywords: [ + "UI component library", + "React components", + "React UI library", + "UI components", + "component library", + "Tailwind CSS components", + "TypeScript components", + "Framer Motion", + "Next.js components", + "animated components", + "copy paste components", + "free UI components", + "open source components", + "modern UI", + "web components", + "frontend components", + "design system", + "Harsh Jadhav", + "Harsh Jadhav developer", + "Harsh Jadhav portfolio", + "harshjdhv", + "React developer", + "frontend developer", + "shadcn alternative", + "radix ui", + "beautiful UI", + "premium components", + "handcrafted components", + ], + authors: [ + { name: "Harsh Jadhav", url: "https://twitter.com/harshjdhv" }, + { name: "Harsh Jadhav", url: "https://github.com/harshjdhv" }, + ], + creator: "Harsh Jadhav", + publisher: "Harsh Jadhav", + formatDetection: { + email: false, + address: false, + telephone: false, + }, + alternates: { + canonical: siteUrl, + }, openGraph: { - title: "Component Playground", - description: "A personal workshop for handcrafted UI components.", - siteName: "Component Playground", + type: "website", + locale: "en_US", + url: siteUrl, + title: "Componentry - Premium React UI Component Library by Harsh Jadhav", + description: "Free, open-source React UI components. Beautiful, animated, copy-paste components built with Tailwind CSS, TypeScript & Framer Motion by Harsh Jadhav.", + siteName: "Componentry", images: [ { - url: "/preview.png", + url: `${siteUrl}/preview-wa.jpg`, width: 1200, height: 630, - alt: "Component Playground Preview", - type: "image/png", + alt: "Componentry - Premium React UI Component Library", + type: "image/jpeg", }, ], }, twitter: { card: "summary_large_image", - title: "Component Playground", - description: "A personal workshop for handcrafted UI components.", - images: ["/preview.png"], + title: "Componentry - Premium React UI Component Library", + description: "Free, open-source React UI components by Harsh Jadhav. Beautiful, animated, copy-paste components.", + images: [ + { + url: `${siteUrl}/preview-wa.jpg`, + width: 1200, + height: 630, + alt: "Componentry - Premium React UI Component Library", + }, + ], + creator: "@harshjdhv", + site: "@harshjdhv", + }, + robots: { + index: true, + follow: true, + nocache: false, + googleBot: { + index: true, + follow: true, + noimageindex: false, + "max-video-preview": -1, + "max-image-preview": "large", + "max-snippet": -1, + }, }, icons: { icon: "/icon.svg", shortcut: "/icon.svg", apple: "/icon.svg", }, + manifest: "/manifest.json", + category: "technology", + classification: "UI Component Library", + other: { + "msapplication-TileImage": "/preview.png", + ...(process.env.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION && { + "google-site-verification": process.env.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION, + }), + }, } export default function RootLayout({ @@ -59,10 +153,14 @@ export default function RootLayout({ }>): React.JSX.Element { return ( + + + {children} + ) diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 7ef0027..2e08e5d 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -2,151 +2,811 @@ import type React from "react" import Link from "next/link" -import { useEffect, useState } from "react" +import { useEffect, useState, useRef } from "react" +import { motion, useScroll, useTransform, useInView } from "framer-motion" +import { ArrowRight, Sparkles, Layers, Zap, Box, Github, ArrowUpRight, Copy, Palette, Code2, Blocks, MousePointerClick, Gauge, Users, Star, GitFork, Download } from "lucide-react" +import { Logomark } from "@/components/logos/logomark" +import Lenis from "lenis" -function GridBackground() { +function useSmoothScroll() { + useEffect(() => { + const lenis = new Lenis({ + duration: 1.2, + easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), + smoothWheel: true, + }) + + function raf(time: number) { + lenis.raf(time) + requestAnimationFrame(raf) + } + + requestAnimationFrame(raf) + + return () => { + lenis.destroy() + } + }, []) +} + +function NoiseOverlay() { + return ( +
+ + + + + + + +
+ ) +} + +function GradientOrbs() { + return ( +
+ + + +
+ ) +} + +function GridLines() { return ( -
+
+
+
+ ) +} + +function AnimatedText({ text, className = "" }: { text: string; className?: string }) { + return ( + + {text.split("").map((char, i) => ( + + {char === " " ? "\u00A0" : char} + + ))} + + ) +} + +function FloatingElement({ children, className = "", delay = 0 }: { children: React.ReactNode; className?: string; delay?: number }) { + return ( + + {children} + + ) +} + +function OrbitingElement({ className = "", duration = 20, radius = 300, startAngle = 0 }: { className?: string; duration?: number; radius?: number; startAngle?: number }) { + return ( + + + + ) +} + +function CodePreviewCard({ className = "", delay = 0 }: { className?: string; delay?: number }) { + return ( + + +
+
+
+
+ component.tsx +
+
+
{"// Premium UI"}
+
import {"{ Button }"}
+
{"// ..."}
+
+ {"<"} + Button + {" "} + variant + {"="} + {'"shine"'} + {">"} +
+
Click me
+
+ {" + Button + {">"} +
+
+ + + ) +} + +function ComponentPreviewCard({ className = "", delay = 0 }: { className?: string; delay?: number }) { + return ( + + +
+
+
+ +
+
+
Border Beam
+
Animated effect
+
+
+
+ +
+
Preview
+
+
+
+
Copy
+
Use
+
+
+
+
+ ) +} + +function FloatingBadge({ text, className = "", delay = 0 }: { text: string; className?: string; delay?: number }) { + return ( + + + {text} + + + ) +} + +function ConnectingLines() { + return ( + + + + -
-
+ + + ) +} + +function MarqueeItem({ children }: { children: React.ReactNode }) { + return ( +
+ {children}
) } -function FloatingCard({ className, offset = 0 }: { className?: string; offset?: number }) { - const [y, setY] = useState(0) +function InfiniteMarquee() { + const items = [ + "Handcrafted with precision", + "Built for developers", + "Open source forever", + "Pixel-perfect details", + "Smooth animations", + "Dark mode ready", + "TypeScript first", + "Tailwind powered", + ] + return ( +
+
+
+ + {[...items, ...items, ...items, ...items].map((item, i) => ( + + + {item} + + ))} + +
+ ) +} + +function FeatureCard({ icon: Icon, title, description, delay = 0 }: { icon: React.ElementType; title: string; description: string; delay?: number }) { + return ( + +
+
+
+
+ +
+

{title}

+

{description}

+
+
+ + ) +} + +function BentoCard({ children, className = "", delay = 0 }: { children: React.ReactNode; className?: string; delay?: number }) { + return ( + + {children} + + ) +} + +function AnimatedCounter({ value, suffix = "", prefix = "" }: { value: number; suffix?: string; prefix?: string }) { + const ref = useRef(null) + const inView = useInView(ref, { once: true, margin: "-100px" }) + const [count, setCount] = useState(0) + useEffect(() => { - let frame: number - const animate = () => { - setY(Math.sin((Date.now() + offset * 1000) / 1000) * 10) - frame = requestAnimationFrame(animate) + if (inView) { + const duration = 2000 + const steps = 60 + const increment = value / steps + let current = 0 + const timer = setInterval(() => { + current += increment + if (current >= value) { + setCount(value) + clearInterval(timer) + } else { + setCount(Math.floor(current)) + } + }, duration / steps) + return () => clearInterval(timer) } - animate() - return () => cancelAnimationFrame(frame) - }, [offset]) - + }, [inView, value]) + + return ( + + {prefix}{count.toLocaleString()}{suffix} + + ) +} + +function StatCard({ icon: Icon, value, suffix, label, delay = 0 }: { icon: React.ElementType; value: number; suffix?: string; label: string; delay?: number }) { return ( -
-
-
-
+
+
-
+
+ +
+
{label}
+ ) } -function FadeIn({ children, delay = 0, className = "" }: { children: React.ReactNode; delay?: number; className?: string }) { - const [visible, setVisible] = useState(false) - - useEffect(() => { - const timer = setTimeout(() => setVisible(true), delay) - return () => clearTimeout(timer) - }, [delay]) - +function TestimonialCard({ quote, author, role, delay = 0 }: { quote: string; author: string; role: string; delay?: number }) { return ( -
- {children} -
+
+
+ {[...Array(5)].map((_, i) => ( + + ))} +
+

“{quote}”

+
+
+ {author[0]} +
+
+
{author}
+
{role}
+
+
+
+ ) } export default function Page(): React.JSX.Element { + useSmoothScroll() + const { scrollYProgress } = useScroll() + const heroOpacity = useTransform(scrollYProgress, [0, 0.2], [1, 0]) + const heroScale = useTransform(scrollYProgress, [0, 0.2], [1, 0.95]) + return ( -
- +
+ + + - - - - + {/* Navigation */} + +
+ +
+ +
+ Componentry + +
+ + Components + + + + GitHub + +
+
+
-
-
- - - Experiments in UI + {/* Hero Section */} + +
+ + + + Now Open Source - - - -

+ +

+ +
+ - Component -
- Playground -

-
- - -

- A personal workshop for handcrafted UI components. Not a design - system. Just experiments, ideas, and iterations built in public. -

-
- - + Components + + + + + A curated collection of handcrafted React components. Meticulously designed, + beautifully animated, and built for modern interfaces. + + + - Browse Components - - - + Explore Components + + - - - - GitHub + + Star on GitHub - +
+
+ + {/* Connecting Lines */} + - -
-
- Actively building + {/* Left Side Elements */} + + + +
+ +
+
+ + + {/* Right Side Elements */} + + + +
+ +
+
+ + + {/* Small Floating Icons */} + +
+ +
+
+ +
+ +
+
+ + {/* Orbiting Dots */} +
+ + + + +
+ + {/* Scroll Indicator */} + + + + + + + + {/* Marquee */} +
+ +
+ + {/* Features Section */} +
+
+ + + Why Componentry + +

+ Built for developers who care +

+

+ Every component is crafted with attention to detail, performance, and developer experience. +

+
+ +
+ + + + + + +
+
+
+ + {/* Testimonials */} +
+
+ + + Testimonials + +

+ Loved by developers +

+

+ See what others are saying about Componentry. +

+
+ +
+ + + +
+
+
+ + {/* CTA Section */} +
+
+ +

+ Start building today +

+

+ Copy, paste, and customize. Every component is yours to use and modify freely. +

+
+ + Get Started + + + + View Source + +
-
- Progress > polish - +
-
+ -
-

Built with care, shipped with curiosity

+ {/* Footer */} +
+
+
+
+ +
+ Built with obsession by Harsh +
+
+ Components + GitHub + Twitter +
+
) diff --git a/apps/web/app/preview/page.tsx b/apps/web/app/preview/page.tsx index 147a058..01ec08f 100644 --- a/apps/web/app/preview/page.tsx +++ b/apps/web/app/preview/page.tsx @@ -1,101 +1,219 @@ import type React from "react" -import { cn } from "@workspace/ui/lib/utils" +import { Sparkles, Layers, Zap, Box } from "lucide-react" -function BackgroundGrid({ className }: { className?: string }) { +function NoiseOverlay() { return ( - - - - - - - - +
+ + + + + + + +
+ ) +} + +function GradientOrbs() { + return ( +
+
+
+
+
) } -function HalftoneEffect({ className }: { className?: string }) { +function GridLines() { return ( - - - - - - - - - - - - - - +
+
+
+ ) +} + +function CodePreviewCard({ className = "" }: { className?: string }) { + return ( +
+
+
+
+
+
+ component.tsx +
+
+
{"// Premium UI"}
+
import {"{ Button }"}
+
{"// ..."}
+
+ {"<"} + Button + {" "} + variant + {"="} + {'"shine"'} + {">"} +
+
Click me
+
+ {" + Button + {">"} +
+
+
+
+ ) +} + +function ComponentPreviewCard({ className = "" }: { className?: string }) { + return ( +
+
+
+
+
+ +
+
+
Border Beam
+
Animated effect
+
+
+
+
+
Preview
+
+
+
+
Copy
+
Use
+
+
+
+
+ ) +} + +function FloatingBadge({ text, className = "" }: { text: string; className?: string }) { + return ( +
+
+ {text} +
+
+ ) +} + +function FloatingIcon({ icon: Icon, className = "" }: { icon: React.ElementType; className?: string }) { + return ( +
+
+ +
+
+ ) +} + +function ConnectingLines() { + return ( + + + + + ) } export default function PreviewPage(): React.JSX.Element { return ( -
- {/* Backgrounds */} - - - - {/* Frame Border */} -
- - {/* Main Layout */} -
- {/* Center Content */} -
-
-

+ + + + + + {/* Border Frame */} +
+ + {/* Heading Glow */} +
+ + {/* Main Content */} +
+
+ {/* Badge */} +
+ + + Now Open Source + +
+ + {/* Heading */} +

+ - Component -
- Playground -

+ Premium + + UI +
+ Components +

+ + {/* Divider Line */} +
+
- -
- -

- Precision Crafted React Components + + {/* Tagline */} +

+ Handcrafted React components. Beautifully animated.

-
+
+ + {/* Floating Elements - Larger for visibility */} + + + + + + {/* Corner Icons */} + + + +
) } diff --git a/apps/web/app/robots.ts b/apps/web/app/robots.ts new file mode 100644 index 0000000..290be0f --- /dev/null +++ b/apps/web/app/robots.ts @@ -0,0 +1,27 @@ +import { MetadataRoute } from "next" + +export default function robots(): MetadataRoute.Robots { + const baseUrl = "https://componentry.fun" + + return { + rules: [ + { + userAgent: "*", + allow: "/", + disallow: ["/api/", "/_next/", "/preview"], + }, + { + userAgent: "Googlebot", + allow: "/", + disallow: ["/api/", "/_next/"], + }, + { + userAgent: "Bingbot", + allow: "/", + disallow: ["/api/", "/_next/"], + }, + ], + sitemap: `${baseUrl}/sitemap.xml`, + host: baseUrl, + } +} diff --git a/apps/web/app/sitemap.ts b/apps/web/app/sitemap.ts new file mode 100644 index 0000000..185534d --- /dev/null +++ b/apps/web/app/sitemap.ts @@ -0,0 +1,48 @@ +import { MetadataRoute } from "next" + +const baseUrl = "https://componentry.fun" + +export default function sitemap(): MetadataRoute.Sitemap { + const currentDate = new Date() + + const staticPages: MetadataRoute.Sitemap = [ + { + url: baseUrl, + lastModified: currentDate, + changeFrequency: "weekly", + priority: 1, + }, + { + url: `${baseUrl}/docs`, + lastModified: currentDate, + changeFrequency: "weekly", + priority: 0.9, + }, + { + url: `${baseUrl}/preview`, + lastModified: currentDate, + changeFrequency: "monthly", + priority: 0.5, + }, + ] + + const componentPages = [ + "flight-status-card", + "border-beam", + "spotlight-card", + "circuit-board", + "command-menu", + "dither-gradient", + "liquid-blob", + "noise-texture", + ] + + const componentSitemap: MetadataRoute.Sitemap = componentPages.map((component) => ({ + url: `${baseUrl}/docs/components/${component}`, + lastModified: currentDate, + changeFrequency: "weekly" as const, + priority: 0.8, + })) + + return [...staticPages, ...componentSitemap] +} diff --git a/apps/web/components/code-block.tsx b/apps/web/components/code-block.tsx index 6f53493..1ae57c9 100644 --- a/apps/web/components/code-block.tsx +++ b/apps/web/components/code-block.tsx @@ -1,4 +1,5 @@ import { codeToHtml } from "shiki" +import { CopyButton } from "./copy-button" interface CodeBlockProps { code: string @@ -57,9 +58,11 @@ export async function CodeBlock({ code, lang = "tsx", className }: CodeBlockProp } `}
+ className={`relative rounded-lg text-sm w-full [&_pre]:p-4 [&_pre]:overflow-x-auto bg-zinc-100 dark:bg-zinc-900 max-h-[400px] overflow-auto ${className || ""}`} + > + +
+
) } diff --git a/apps/web/components/command-menu.tsx b/apps/web/components/command-menu.tsx index cbe218b..cdcb5de 100644 --- a/apps/web/components/command-menu.tsx +++ b/apps/web/components/command-menu.tsx @@ -1,15 +1,18 @@ "use client" import * as React from "react" +import * as ReactDOM from "react-dom" import { useRouter } from "next/navigation" import { Command } from "cmdk" -import { Search } from "lucide-react" +import { Search, FileText, Hash, ArrowRight } from "lucide-react" +import { motion, AnimatePresence } from "framer-motion" import { docsConfig } from "@/config/docs" -import { cn } from "@workspace/ui/lib/utils" export function CommandMenu() { const router = useRouter() const [open, setOpen] = React.useState(false) + const [query, setQuery] = React.useState("") + const inputRef = React.useRef(null) React.useEffect(() => { const down = (e: KeyboardEvent) => { @@ -17,12 +20,25 @@ export function CommandMenu() { e.preventDefault() setOpen((open) => !open) } + if (e.key === "Escape") { + setOpen(false) + } } document.addEventListener("keydown", down) return () => document.removeEventListener("keydown", down) }, []) + React.useEffect(() => { + if (open) { + setTimeout(() => { + inputRef.current?.focus() + }, 0) + } else { + setQuery("") + } + }, [open]) + const runCommand = React.useCallback((command: () => unknown) => { setOpen(false) command() @@ -32,51 +48,131 @@ export function CommandMenu() { <> - -
- - -
- - No results found. - {docsConfig.nav.map((group) => ( - - {group.items.map((navItem) => ( - { - runCommand(() => router.push(navItem.href)) - }} - className="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50" - > - - {navItem.title} - - ))} - - ))} - -
- - {open && ( -
setOpen(false)} /> + + {typeof document !== "undefined" && ReactDOM.createPortal( + + {open && ( + <> + setOpen(false)} + /> + + +
+
+ +
+ + {query && ( + setQuery("")} + className="rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-accent hover:text-accent-foreground transition-colors" + > + Clear + + )} + + ESC + +
+ + + +
+ +
+

No results found

+

Try searching for something else

+
+ + {docsConfig.nav.map((group) => ( + + {group.items.map((navItem) => ( + { + runCommand(() => router.push(navItem.href)) + }} + className="group/item relative flex cursor-pointer select-none items-center gap-3 rounded-xl px-3 py-2.5 text-sm outline-none transition-colors hover:bg-accent/70 hover:text-accent-foreground aria-[selected='true']:bg-accent aria-[selected='true']:text-accent-foreground data-[disabled='true']:pointer-events-none data-[disabled='true']:opacity-50" + > +
+ {group.title === "Getting Started" ? ( + + ) : ( + + )} +
+
+ {navItem.title} + {group.title} +
+ +
+ ))} +
+ ))} +
+ +
+
+ + ↑↓ + Navigate + + + + Select + +
+ Componentry +
+
+
+ + )} +
, + document.body )} ) diff --git a/apps/web/components/component-layout.tsx b/apps/web/components/component-layout.tsx new file mode 100644 index 0000000..dbf5aa7 --- /dev/null +++ b/apps/web/components/component-layout.tsx @@ -0,0 +1,58 @@ +import type React from "react" +import { cn } from "@workspace/ui/lib/utils" + +interface ComponentLayoutProps { + title: string + description: string + children: React.ReactNode +} + +export function ComponentLayout({ + title, + description, + children, +}: ComponentLayoutProps) { + return ( +
+
+
+
+

+ {title} +

+

{description}

+
+
+ + {children} +
+ ) +} + +interface SectionProps { + title: string + children: React.ReactNode + id?: string +} + +export function Section({ title, children, id }: SectionProps) { + const sectionId = id || title.toLowerCase().replace(/\s+/g, "-") + + return ( +
+
+ {title} +
+
+ {children} +
+
+ ) +} diff --git a/apps/web/components/copy-button.tsx b/apps/web/components/copy-button.tsx new file mode 100644 index 0000000..73e3963 --- /dev/null +++ b/apps/web/components/copy-button.tsx @@ -0,0 +1,32 @@ +"use client" + +import { useState } from "react" +import { Check, Copy } from "lucide-react" + +interface CopyButtonProps { + code: string +} + +export function CopyButton({ code }: CopyButtonProps) { + const [copied, setCopied] = useState(false) + + const handleCopy = async () => { + await navigator.clipboard.writeText(code) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } + + return ( + + ) +} diff --git a/apps/web/components/docs-sidebar.tsx b/apps/web/components/docs-sidebar.tsx index 172573b..244f39a 100644 --- a/apps/web/components/docs-sidebar.tsx +++ b/apps/web/components/docs-sidebar.tsx @@ -16,7 +16,7 @@ export function DocsSidebar() {

{group.title}

-
    +
      {group.items.map((item) => { const isActive = pathname === item.href return ( @@ -24,7 +24,7 @@ export function DocsSidebar() { diff --git a/apps/web/components/seo/json-ld.tsx b/apps/web/components/seo/json-ld.tsx new file mode 100644 index 0000000..9b378e5 --- /dev/null +++ b/apps/web/components/seo/json-ld.tsx @@ -0,0 +1,143 @@ +export function JsonLd() { + const siteUrl = "https://componentry.fun" + + const organizationSchema = { + "@context": "https://schema.org", + "@type": "Organization", + name: "Componentry", + url: siteUrl, + logo: `${siteUrl}/icon.svg`, + sameAs: [ + "https://github.com/harshjdhv/componentry", + "https://twitter.com/harshjdhv", + ], + founder: { + "@type": "Person", + name: "Harsh Jadhav", + url: "https://twitter.com/harshjdhv", + sameAs: [ + "https://twitter.com/harshjdhv", + "https://github.com/harshjdhv", + ], + }, + } + + const websiteSchema = { + "@context": "https://schema.org", + "@type": "WebSite", + name: "Componentry", + alternateName: ["Componentry UI", "Componentry Components"], + url: siteUrl, + description: + "Free, open-source React UI component library by Harsh Jadhav. Beautiful, animated, copy-paste components.", + publisher: { + "@type": "Person", + name: "Harsh Jadhav", + url: "https://twitter.com/harshjdhv", + }, + potentialAction: { + "@type": "SearchAction", + target: { + "@type": "EntryPoint", + urlTemplate: `${siteUrl}/docs?search={search_term_string}`, + }, + "query-input": "required name=search_term_string", + }, + } + + const softwareApplicationSchema = { + "@context": "https://schema.org", + "@type": "SoftwareSourceCode", + name: "Componentry", + description: + "Premium React UI component library with beautiful animations. Copy-paste components built with Tailwind CSS, TypeScript, and Framer Motion.", + url: siteUrl, + codeRepository: "https://github.com/harshjdhv/componentry", + programmingLanguage: ["TypeScript", "JavaScript", "React", "CSS"], + runtimePlatform: "Node.js", + author: { + "@type": "Person", + name: "Harsh Jadhav", + url: "https://twitter.com/harshjdhv", + }, + license: "https://opensource.org/licenses/MIT", + operatingSystem: "Cross-platform", + applicationCategory: "DeveloperApplication", + keywords: + "React, UI components, Tailwind CSS, TypeScript, Framer Motion, Next.js, component library", + } + + const personSchema = { + "@context": "https://schema.org", + "@type": "Person", + name: "Harsh Jadhav", + alternateName: ["harshjdhv", "Harsh"], + url: "https://twitter.com/harshjdhv", + jobTitle: "Frontend Developer", + knowsAbout: [ + "React", + "TypeScript", + "JavaScript", + "Tailwind CSS", + "Next.js", + "UI/UX Design", + "Web Development", + "Frontend Development", + ], + sameAs: [ + "https://twitter.com/harshjdhv", + "https://github.com/harshjdhv", + siteUrl, + ], + mainEntityOfPage: { + "@type": "WebPage", + "@id": siteUrl, + }, + } + + const breadcrumbSchema = { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + itemListElement: [ + { + "@type": "ListItem", + position: 1, + name: "Home", + item: siteUrl, + }, + { + "@type": "ListItem", + position: 2, + name: "Components", + item: `${siteUrl}/docs`, + }, + ], + } + + return ( + <> +