V1/release 16 Dec 2025 (#4)

* v1 prepration

* Fix baseui tooltip components

* Update native-liquid-button.tsx

* Resumes fix

* Add uitripled CLI package and update install tabs

Introduces a new uitripled CLI package for installing animated UI components, including CLI source, documentation, and publish script. Updates the AnimationDetailPage to feature install instructions for both shadcn and uitripled, replacing the previous npx/yarn/pnpm tabs with shadcn/uitripled options and corresponding copy-to-clipboard functionality.

* update package release

* Update package.json

* Hotfix
This commit is contained in:
Moumen Soliman 2025-12-16 02:52:02 +02:00 committed by GitHub
parent e23ae85528
commit 9ee2f7530a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1050 changed files with 72511 additions and 6638 deletions

10
.eslintrc Normal file
View file

@ -0,0 +1,10 @@
{
"plugins": ["unused-imports"],
"rules": {
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn",
{ "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" }
]
}
}

1
.gitignore vendored
View file

@ -9,6 +9,7 @@
!.yarn/plugins
!.yarn/releases
!.yarn/versions
.npmrc
# testing
/coverage

View file

@ -6,5 +6,6 @@
"tabWidth": 2,
"useTabs": false,
"arrowParens": "always",
"endOfLine": "lf"
"endOfLine": "lf",
"plugins": ["prettier-plugin-organize-imports"]
}

View file

@ -1,5 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import JSZip from "jszip";
import { NextRequest, NextResponse } from "next/server";
type PageFile = {
path: string;

View file

@ -1,7 +1,7 @@
import { NextRequest, NextResponse } from "next/server";
import { readFileSync, existsSync } from "fs";
import { join } from "path";
import registryIndex from "@/registry.json";
import { existsSync, readFileSync } from "fs";
import { NextRequest, NextResponse } from "next/server";
import { join } from "path";
type RegistryItem = {
name: string;

View file

@ -1,6 +1,25 @@
"use client";
import { useState, useEffect, useCallback, useMemo } from "react";
import { BuilderCanvas } from "@/components/builder-canvas";
import { BuilderCodeView } from "@/components/builder-code-view";
import { BuilderSidebar } from "@/components/builder-sidebar";
import { BuilderHeader } from "@/components/builder/builder-header";
import { DragOverlay } from "@/components/builder/drag-overlay";
import { InstructionsBanner } from "@/components/builder/instructions-banner";
import { LoadProjectDialog } from "@/components/builder/load-project-dialog";
import { PageTabs } from "@/components/builder/page-tabs";
import { TextEditingBanner } from "@/components/builder/text-editing-banner";
import {
createPage,
extractSavedPages,
generateUniqueSlug,
} from "@/lib/builder-utils";
import { componentsRegistry } from "@/lib/components-registry";
import type {
BuilderComponent,
BuilderProjectPage,
SavedProject,
} from "@/types/builder";
import {
DndContext,
DragEndEvent,
@ -10,26 +29,7 @@ import {
useSensors,
} from "@dnd-kit/core";
import { arrayMove } from "@dnd-kit/sortable";
import { BuilderSidebar } from "@/components/builder-sidebar";
import { BuilderCanvas } from "@/components/builder-canvas";
import { BuilderCodeView } from "@/components/builder-code-view";
import { componentsRegistry } from "@/lib/components-registry";
import {
createPage,
generateUniqueSlug,
extractSavedPages,
} from "@/lib/builder-utils";
import type {
BuilderComponent,
BuilderProjectPage,
SavedProject,
} from "@/types/builder";
import { BuilderHeader } from "@/components/builder/builder-header";
import { InstructionsBanner } from "@/components/builder/instructions-banner";
import { TextEditingBanner } from "@/components/builder/text-editing-banner";
import { PageTabs } from "@/components/builder/page-tabs";
import { LoadProjectDialog } from "@/components/builder/load-project-dialog";
import { DragOverlay } from "@/components/builder/drag-overlay";
import { useCallback, useEffect, useMemo, useState } from "react";
export default function BuilderPage() {
const [pages, setPages] = useState<BuilderProjectPage[]>(() => {

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
import Link from "next/link";
import { ArrowLeft } from "lucide-react";
import Link from "next/link";
export default function NotFound() {
return (

View file

@ -1,12 +1,11 @@
import AnimationDetailPageClient from "./AnimationDetailPage.client";
import { createMetadata } from "@/lib/seo";
import {
getComponentById,
componentsRegistry,
getComponentById,
loadComponentCode,
} from "@/lib/components-registry";
import { Component } from "@/types";
import { createMetadata } from "@/lib/seo";
import { notFound } from "next/navigation";
import AnimationDetailPageClient from "./AnimationDetailPage.client";
type PageParams = {
params: Promise<{
@ -22,7 +21,11 @@ export function generateStaticParams() {
export const dynamicParams = true;
export async function generateMetadata({ params }: PageParams) {
import { Metadata } from "next";
export async function generateMetadata({
params,
}: PageParams): Promise<Metadata> {
const { id } = await params;
const component = getComponentById(id);
@ -84,12 +87,126 @@ export default async function AnimationDetailPage({ params }: PageParams) {
);
}
// Load both baseui and shadcnui code for native components
let baseuiCode: string | undefined;
let shadcnuiCode: string | undefined;
let carbonCode: string | undefined;
let baseuiDemoCode: string | undefined;
let shadcnuiDemoCode: string | undefined;
let carbonDemoCode: string | undefined;
if (component.category === "native") {
const baseuiPath = `@/components/native/baseui/${component.id}-baseui.tsx`;
const shadcnuiPath = `@/components/native/shadcnui/${component.id}-shadcnui.tsx`;
const carbonPath = `@/components/native/carbon/${component.id}-carbon.tsx`;
// Demo paths
const baseuiDemoPath = `@/components/native/baseui/demo/${component.id}-demo.tsx`;
const shadcnuiDemoPath = `@/components/native/shadcnui/demo/${component.id}-demo.tsx`;
const carbonDemoPath = `@/components/native/carbon/demo/${component.id}-demo.tsx`;
try {
baseuiCode = await loadComponentCode({
...component,
codePath: baseuiPath,
});
} catch (error) {
// Baseui version doesn't exist, that's okay
}
try {
baseuiDemoCode = await loadComponentCode({
...component,
codePath: baseuiDemoPath,
});
} catch (error) {
// Baseui demo doesn't exist, that's okay
}
try {
shadcnuiCode = await loadComponentCode({
...component,
codePath: shadcnuiPath,
});
} catch (error) {
// Shadcnui version doesn't exist, that's okay
}
try {
shadcnuiDemoCode = await loadComponentCode({
...component,
codePath: shadcnuiDemoPath,
});
} catch (error) {
// Shadcnui demo doesn't exist, that's okay
}
try {
carbonCode = await loadComponentCode({
...component,
codePath: carbonPath,
});
} catch (error) {
// Carbon version doesn't exist, that's okay
}
try {
carbonDemoCode = await loadComponentCode({
...component,
codePath: carbonDemoPath,
});
} catch (error) {
// Carbon demo doesn't exist, that's okay
}
}
// Load baseui code for non-native components if available
if (
component.category !== "native" &&
component.availableIn &&
component.availableIn.includes("baseui")
) {
// Try different paths based on component category
const possiblePaths = [
`@/components/sections/baseui/${component.id}-baseui.tsx`,
`@/components/components/resumes/baseui/${component.id}-baseui.tsx`,
`@/components/components/cards/baseui/${component.id}-baseui.tsx`,
];
for (const baseuiPath of possiblePaths) {
try {
const loadedBaseuiCode = await loadComponentCode({
...component,
codePath: baseuiPath,
});
if (loadedBaseuiCode) {
baseuiCode = loadedBaseuiCode;
break;
}
} catch (error) {
// Path doesn't exist, try next one
}
}
// Ensure shadcnuiCode is set to the default code if not already set
if (!shadcnuiCode) {
shadcnuiCode = code;
}
}
return (
<AnimationDetailPageClient
code={code}
relatedComponents={relatedComponents}
variantCodes={variantCodes}
baseId={component.id}
baseuiCode={baseuiCode}
shadcnuiCode={shadcnuiCode}
carbonCode={carbonCode}
baseuiDemoCode={baseuiDemoCode}
shadcnuiDemoCode={shadcnuiDemoCode}
carbonDemoCode={carbonDemoCode}
/>
);
}

View file

@ -1,23 +1,22 @@
"use client";
import { useState, useMemo, useEffect, useCallback, Suspense } from "react";
import { useParams, usePathname, useRouter } from "next/navigation";
import { motion, AnimatePresence } from "framer-motion";
import { ChevronLeft, ChevronRight, Menu, X } from "lucide-react";
import { useQueryState, parseAsString } from "nuqs";
import {
getComponentById,
componentsRegistry,
} from "@/lib/components-registry";
import { AnimationsSidebar } from "@/components/animation-sidebar";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogTrigger,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
componentsRegistry,
getComponentById,
} from "@/lib/components-registry";
import { AnimatePresence, motion } from "framer-motion";
import { ChevronLeft, ChevronRight, Menu } from "lucide-react";
import { useParams, usePathname, useRouter } from "next/navigation";
import { parseAsString, useQueryState } from "nuqs";
import { Suspense, useCallback, useEffect, useMemo, useState } from "react";
function ComponentsLayoutContent({ children }: { children: React.ReactNode }) {
const params = useParams();

View file

@ -4,13 +4,23 @@ import { createMetadata } from "@/lib/seo";
export const metadata = createMetadata({
title: "Component Library",
description:
"Browse 70+ production-ready motion components built with Framer Motion, shadcn/ui, and Tailwind CSS.",
"Browse 70+ production-ready motion components built with Framer Motion, shadcn/ui, baseui, and Tailwind CSS.",
path: "/components",
keywords: [
"React components",
"motion components",
"baseui components",
"shadcn components",
"tailwind css",
"component library",
"Framer Motion UI",
"UI TripleD",
"tripled",
"UI components",
"motion components",
"React components",
"Next.js components",
"Framer Motion",
],
});

View file

@ -1,17 +1,17 @@
"use client";
import { useEffect, useRef, useState } from "react";
import { motion, type Variants } from "framer-motion";
import { CodeOutput } from "@/components/grid/code-output";
import { GridPreview } from "@/components/grid/grid-preview";
import { PresetsPanel } from "@/components/grid/presets-panel";
import { SettingsPanel } from "@/components/grid/settings-panel";
import {
generateGridCode,
getCellKey,
initializeCells,
type GridCell,
} from "@/lib/grid-utils";
import { SettingsPanel } from "@/components/grid/settings-panel";
import { PresetsPanel } from "@/components/grid/presets-panel";
import { CodeOutput } from "@/components/grid/code-output";
import { GridPreview } from "@/components/grid/grid-preview";
import { motion, type Variants } from "framer-motion";
import { useEffect, useRef, useState } from "react";
// Animation variants
const containerVariants: Variants = {

View file

@ -1,12 +1,13 @@
import { Footer } from "@/components/footer";
import { Header } from "@/components/header";
import { THEME_STORAGE_KEY, ThemeProvider } from "@/components/theme-provider";
import { UILibraryProvider } from "@/components/ui-library-provider";
import { baseMetadata, siteConfig } from "@/lib/seo";
import type { Viewport } from "next";
import { Inter } from "next/font/google";
import Script from "next/script";
import "./globals.css";
import { Header } from "@/components/header";
import { Footer } from "@/components/footer";
import { ThemeProvider, THEME_STORAGE_KEY } from "@/components/theme-provider";
import { baseMetadata, siteConfig } from "@/lib/seo";
import { NuqsAdapter } from "nuqs/adapters/next/app";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
@ -116,11 +117,13 @@ export default function RootLayout({
async
/>
<ThemeProvider>
<NuqsAdapter>
<Header />
<main className="min-h-screen">{children}</main>
<Footer />
</NuqsAdapter>
<UILibraryProvider>
<NuqsAdapter>
<Header />
<main className="min-h-screen">{children}</main>
<Footer />
</NuqsAdapter>
</UILibraryProvider>
</ThemeProvider>
</body>
</html>

View file

@ -2,16 +2,27 @@ import HomePageContent from "@/components/home-page-content";
import { createMetadata, siteConfig } from "@/lib/seo";
export const metadata = createMetadata({
title: "UI Components, Blocks & Templates",
title: "UI Components, Blocks & Pages",
description: siteConfig.description,
path: "/",
keywords: [
"UI components",
"baseui components",
"shadcn components",
"tailwind css",
"motion components",
"React components",
"Next.js components",
"Framer Motion",
"shadcn/ui",
"Tailwind CSS",
"landing page templates",
"UI library",
"interactive UI",
"UI TripleD",
"tripled",
"UI components",
"motion components",
"React UI library",
"Next.js animations",
"Framer Motion templates",
"shadcn ui components",
],
});

View file

@ -1,14 +1,14 @@
"use client";
import { ComponentType, useEffect, useRef, useState } from "react";
import { useParams, useRouter } from "next/navigation";
import { motion } from "framer-motion";
import { ArrowLeft, Code, ExternalLink, Loader2 } from "lucide-react";
import { CodeBlock } from "@/components/code-block";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { CodeBlock } from "@/components/code-block";
import { componentsRegistry } from "@/lib/components-registry";
import { motion } from "framer-motion";
import { ArrowLeft, Code, ExternalLink, Loader2 } from "lucide-react";
import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
import { ComponentType, useEffect, useRef, useState } from "react";
type SavedProjectComponent = {
id?: string;

View file

@ -1,5 +1,5 @@
import PreviewProjectPageClient from "./PreviewProjectPage.client";
import { createMetadata } from "@/lib/seo";
import PreviewProjectPageClient from "./PreviewProjectPage.client";
type PreviewPageProps = {
params: Promise<{

View file

@ -1,5 +1,5 @@
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { captureRegistryEvent } from "@wandry/analytics-sdk";

View file

@ -1,5 +1,5 @@
import type { MetadataRoute } from "next";
import { siteConfig } from "@/lib/seo";
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {

View file

@ -1,5 +1,5 @@
import type { MetadataRoute } from "next";
import { sitemapEntries } from "@/lib/seo";
import type { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
const lastModified = new Date();

View file

@ -1,9 +1,9 @@
"use client";
import { useState } from "react";
import { Component, categoryNames } from "@/types";
import { motion } from "framer-motion";
import Link from "next/link";
import { Component, categoryNames } from "@/types";
import { useState } from "react";
type AnimationCardProps = {
animation: Component;

View file

@ -1,13 +1,14 @@
"use client";
import { useState, useMemo, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Search, ChevronDown, ChevronRight } from "lucide-react";
import Link from "next/link";
import { useUILibrary } from "@/components/ui-library-provider";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import { componentsRegistry } from "@/lib/components-registry";
import { Component, ComponentCategory, categoryNames } from "@/types";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Input } from "@/components/ui/input";
import { AnimatePresence, motion } from "framer-motion";
import { ChevronDown, ChevronRight, Search } from "lucide-react";
import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
type AnimationsSidebarProps = {
selectedComponent: Component | null;
@ -22,6 +23,7 @@ export function AnimationsSidebar({
useLinks = false,
target,
}: AnimationsSidebarProps) {
const { selectedLibrary } = useUILibrary();
const [searchQuery, setSearchQuery] = useState("");
const [expandedCategories, setExpandedCategories] = useState<
Set<ComponentCategory | "all">
@ -92,6 +94,24 @@ export function AnimationsSidebar({
// First filter by display property (only show animations where display !== false)
let filtered = componentsRegistry.filter((anim) => anim.display !== false);
// Filter by selected UI library
// If availableIn is not specified, component defaults to shadcnui only
// Carbon = pure React, accessible from both shadcnui and baseui
filtered = filtered.filter((anim) => {
const availableLibraries = anim.availableIn || ["shadcnui"];
// If component has "carbon" (pure React), it's compatible with shadcnui and baseui
if (availableLibraries.includes("carbon")) {
return (
selectedLibrary === "shadcnui" ||
selectedLibrary === "baseui" ||
selectedLibrary === "carbon"
);
}
return availableLibraries.includes(selectedLibrary);
});
if (searchQuery) {
const lowerQuery = searchQuery.toLowerCase();
filtered = filtered.filter(
@ -103,7 +123,7 @@ export function AnimationsSidebar({
}
return filtered;
}, [searchQuery]);
}, [searchQuery, selectedLibrary]);
const animationsByCategory = useMemo(() => {
const grouped: Record<ComponentCategory | "all", Component[]> = {

View file

@ -1,8 +1,8 @@
"use client";
import { useId, useMemo } from "react";
import { motion, useReducedMotion, type Transition } from "framer-motion";
import { User } from "lucide-react";
import { useId, useMemo } from "react";
type Avatar = {
id: number;

View file

@ -1,5 +1,6 @@
"use client";
import type { BuilderComponent } from "@/types/builder";
import { useDroppable } from "@dnd-kit/core";
import {
SortableContext,
@ -7,10 +8,9 @@ import {
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { motion, AnimatePresence } from "framer-motion";
import { AnimatePresence, motion } from "framer-motion";
import { X } from "lucide-react";
import { useState, useEffect, useRef, useCallback } from "react";
import type { BuilderComponent } from "@/types/builder";
import { useCallback, useEffect, useRef, useState } from "react";
type CanvasComponentProps = {
component: BuilderComponent;

View file

@ -1,21 +1,6 @@
"use client";
import { useState, useMemo, useCallback, useEffect } from "react";
import { motion } from "framer-motion";
import {
Copy,
Check,
ChevronDown,
ChevronUp,
Save,
Eye,
Rocket,
Download,
Loader2,
} from "lucide-react";
import { CodeBlock } from "./code-block";
import type { BuilderComponent, BuilderProjectPage } from "@/types/builder";
import { mergeComponentImports } from "@/lib/merge-imports";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
@ -26,8 +11,22 @@ import {
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { mergeComponentImports } from "@/lib/merge-imports";
import type { BuilderComponent, BuilderProjectPage } from "@/types/builder";
import { motion } from "framer-motion";
import {
Check,
ChevronDown,
ChevronUp,
Copy,
Download,
Eye,
Loader2,
Save,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react";
import { CodeBlock } from "./code-block";
type BuilderCodeViewProps = {
pages: BuilderProjectPage[];

View file

@ -1,13 +1,13 @@
"use client";
import { useState, useMemo } from "react";
import { useDraggable } from "@dnd-kit/core";
import { motion } from "framer-motion";
import { Search } from "lucide-react";
import { useMemo, useState } from "react";
import { componentsRegistry } from "@/lib/components-registry";
import { categoryNames } from "@/types";
import { cn } from "@/lib/utils";
import { categoryNames } from "@/types";
type ComponentItem = (typeof componentsRegistry)[number];

View file

@ -1,5 +1,6 @@
"use client";
import { BuilderSidebar } from "@/components/builder-sidebar";
import { Button } from "@/components/ui/button";
import {
Dialog,
@ -7,8 +8,7 @@ import {
DialogContent,
DialogTrigger,
} from "@/components/ui/dialog";
import { BuilderSidebar } from "@/components/builder-sidebar";
import { FolderOpen, Menu, Type, X } from "lucide-react";
import { FolderOpen, Menu, X } from "lucide-react";
type BuilderHeaderProps = {
mobileSidebarOpen: boolean;

View file

@ -1,9 +1,9 @@
"use client";
import { motion, AnimatePresence } from "framer-motion";
import { Info } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { AnimatePresence, motion } from "framer-motion";
import { Info } from "lucide-react";
type InstructionsBannerProps = {
show: boolean;

View file

@ -10,9 +10,9 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Eye, Trash2 } from "lucide-react";
import type { SavedProject } from "@/types/builder";
import { extractSavedPages } from "@/lib/builder-utils";
import type { SavedProject } from "@/types/builder";
import { Eye, Trash2 } from "lucide-react";
type LoadProjectDialogProps = {
open: boolean;

View file

@ -1,8 +1,8 @@
"use client";
import { Button } from "@/components/ui/button";
import { Edit3, Plus, Trash2 } from "lucide-react";
import type { BuilderProjectPage } from "@/types/builder";
import { Edit3, Plus, Trash2 } from "lucide-react";
type PageTabsProps = {
pages: BuilderProjectPage[];

View file

@ -1,6 +1,6 @@
"use client";
import { motion, AnimatePresence } from "framer-motion";
import { AnimatePresence, motion } from "framer-motion";
type TextEditingBannerProps = {
show: boolean;

View file

@ -1,10 +1,10 @@
"use client";
import { useState, type ComponentType } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Check, Copy } from "lucide-react";
import { motion, AnimatePresence } from "framer-motion";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { useState, type ComponentType } from "react";
import type { SyntaxHighlighterProps } from "react-syntax-highlighter";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
const PrismSyntaxHighlighter =

View file

@ -1,7 +1,6 @@
"use client";
import { useState, useEffect } from "react";
import { Palette, Check } from "lucide-react";
import { useTheme } from "@/components/theme-provider";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
@ -12,7 +11,8 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { cn } from "@/lib/utils";
import { useTheme } from "@/components/theme-provider";
import { Check, Palette } from "lucide-react";
import { useEffect, useState } from "react";
type ColorScheme = {
name: string;

View file

@ -1,8 +1,8 @@
"use client";
import { useId, useMemo, useState } from "react";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { Check, ChevronDown, LogOut } from "lucide-react";
import { useId, useMemo, useState } from "react";
type Account = {
id: string;

View file

@ -1,9 +1,9 @@
"use client";
import type React from "react";
import { motion, type Variants } from "framer-motion";
import { ExternalLink, Quote, Code, Image as ImageIcon } from "lucide-react";
import { Code, ExternalLink, Quote } from "lucide-react";
import Image from "next/image";
import type React from "react";
// ============================================================================
// TYPES & INTERFACES

View file

@ -0,0 +1,95 @@
"use client";
import { motion, useReducedMotion } from "framer-motion";
const cards = [
{ title: "Card 1", description: "First card" },
{ title: "Card 2", description: "Second card" },
{ title: "Card 3", description: "Third card" },
];
export function AnimatedCardStackBaseUI() {
const shouldReduceMotion = useReducedMotion();
return (
<div className="relative flex items-center justify-center px-8 py-16">
<div aria-hidden className="pointer-events-none absolute inset-0 -z-10">
<motion.div
className="absolute left-1/2 top-10 h-56 w-56 -translate-x-1/2 rounded-full bg-primary/20 blur-[140px]"
animate={
shouldReduceMotion
? undefined
: { opacity: [0.25, 0.45, 0.25], scale: [0.9, 1.05, 0.95] }
}
transition={
shouldReduceMotion
? undefined
: { duration: 11, repeat: Infinity, ease: "easeInOut" }
}
/>
<motion.div
className="absolute bottom-8 right-12 h-48 w-48 rounded-full bg-emerald-400/25 blur-[150px]"
animate={
shouldReduceMotion
? undefined
: { opacity: [0.18, 0.35, 0.18], rotate: [0, 12, 0] }
}
transition={
shouldReduceMotion
? undefined
: { duration: 13, repeat: Infinity, ease: "linear" }
}
/>
</div>
{cards.map((card, index) => {
const baseScale = 1 - index * 0.04;
const baseOffset = index * 18;
const hoverMotion = shouldReduceMotion
? undefined
: { scale: baseScale + 0.06, y: -20 };
return (
<motion.div
key={card.title}
initial={{ scale: baseScale, y: baseOffset, opacity: 0 }}
animate={{ scale: baseScale, y: baseOffset, opacity: 1 }}
whileHover={hoverMotion}
whileFocus={hoverMotion}
transition={{
type: shouldReduceMotion ? "tween" : "spring",
stiffness: 260,
damping: 26,
delay: index * 0.08,
}}
className="absolute w-64 rounded-3xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
style={{ zIndex: cards.length - index }}
tabIndex={0}
aria-label={`${card.title}: ${card.description}. Hover or focus to expand this card.`}
role="group"
>
{/* Card replacement */}
<div className="rounded-3xl border border-border/60 bg-card/80 shadow-[0_20px_70px_-40px_rgba(15,23,42,0.7)] backdrop-blur-xl transition-shadow duration-300 group-hover:shadow-[0_26px_90px_-45px_rgba(15,23,42,0.8)]">
{/* CardHeader replacement */}
<div className="flex flex-col space-y-1.5 p-6">
{/* CardTitle replacement */}
<h3 className="text-base font-semibold text-foreground">
{card.title}
</h3>
{/* CardDescription replacement */}
<p className="text-xs uppercase tracking-[0.32em] text-muted-foreground/80">
{card.description}
</p>
</div>
{/* CardContent replacement */}
<div className="p-6 pt-0">
<p className="text-sm text-muted-foreground">
Hover or focus to surface this panel and bring it to the
front.
</p>
</div>
</div>
</motion.div>
);
})}
</div>
);
}

View file

@ -0,0 +1,200 @@
"use client";
import {
motion,
useMotionValue,
useReducedMotion,
useSpring,
useTransform,
} from "framer-motion";
import { CreditCard as CreditCardIcon } from "lucide-react";
import { useMemo, useState } from "react";
interface CreditCardBaseUIProps {
cardNumber?: string;
cardholderName?: string;
expiryDate?: string;
cvv?: string;
}
const gradients = {
front:
"linear-gradient(145deg, hsl(var(--primary) / 0.45), hsl(var(--primary) / 0.18))",
back: "linear-gradient(145deg, hsl(var(--primary) / 0.45), hsl(var(--primary) / 0.18))",
};
const overlayLights = [
"radial-gradient(circle at 20% 30%, hsl(var(--foreground) / 0.12), transparent 55%)",
"radial-gradient(circle at 80% 20%, hsl(var(--primary) / 0.22), transparent 60%)",
"radial-gradient(circle at 50% 80%, hsl(var(--accent) / 0.28), transparent 65%)",
];
export function CreditCardBaseUI({
cardNumber = "4532 1234 5678 9010",
cardholderName = "JORDAN PARK",
expiryDate = "09/27",
cvv = "123",
}: CreditCardBaseUIProps) {
const [isFlipped, setIsFlipped] = useState(false);
const x = useMotionValue(0);
const y = useMotionValue(0);
const rotateX = useSpring(useTransform(y, [-0.5, 0.5], [6, -6]), {
stiffness: 280,
damping: 28,
});
const rotateY = useSpring(useTransform(x, [-0.5, 0.5], [-6, 6]), {
stiffness: 280,
damping: 28,
});
const shouldReduceMotion = useReducedMotion();
const overlayGradient = useMemo(() => overlayLights.join(","), []);
const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
if (shouldReduceMotion) return;
const rect = event.currentTarget.getBoundingClientRect();
const width = rect.width;
const height = rect.height;
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
const xPct = mouseX / width - 0.5;
const yPct = mouseY / height - 0.5;
x.set(xPct);
y.set(yPct);
};
const handleMouseLeave = () => {
x.set(0);
y.set(0);
};
const formatCardNumber = (number: string) => {
return number.replace(/(.{4})/g, "$1 ").trim();
};
return (
<div className="flex items-center justify-center p-8 perspective-1000">
<motion.div
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
onClick={() => setIsFlipped(!isFlipped)}
className="relative h-[240px] w-[380px] cursor-pointer"
style={{
rotateX,
rotateY,
transformStyle: "preserve-3d",
}}
whileHover={shouldReduceMotion ? undefined : { scale: 1.015 }}
transition={{ type: "spring", stiffness: 260, damping: 26 }}
>
<div className="absolute inset-0 rounded-[1.5rem] bg-foreground/10 blur-2xl" />
{/* Front */}
<motion.div
className="absolute inset-0 flex flex-col justify-between rounded-[1.5rem] p-6 shadow-[0_22px_55px_-30px_rgba(15,23,42,0.75)]"
style={{
background: gradients.front,
backfaceVisibility: "hidden",
WebkitBackfaceVisibility: "hidden",
}}
animate={{ rotateY: isFlipped ? 180 : 0 }}
transition={{ duration: 0.55, ease: "easeInOut" }}
>
<div
aria-hidden
className="pointer-events-none absolute inset-0 rounded-[1.5rem]"
style={{
background: overlayGradient,
opacity: shouldReduceMotion ? 0.5 : 1,
}}
/>
<div className="relative flex items-start justify-between">
<div className="relative">
<div className="flex h-12 w-14 items-center justify-center rounded-lg border border-border/60 bg-foreground/15 backdrop-blur">
<div className="h-8 w-10 rounded-md border border-border/40 bg-background/60" />
</div>
<div className="absolute -bottom-1 left-0 right-0 h-1 rounded-full bg-foreground/10" />
</div>
</div>
<div className="relative space-y-2">
<span className="text-[10px] uppercase tracking-[0.42em] text-muted-foreground/70">
Card Number
</span>
<p className="font-mono text-2xl tracking-[0.4em] text-foreground/90">
{formatCardNumber(cardNumber)}
</p>
</div>
<div className="relative flex items-end justify-between">
<div className="space-y-1">
<span className="text-[10px] uppercase tracking-[0.36em] text-muted-foreground/70">
Cardholder
</span>
<p className="text-sm font-semibold uppercase tracking-[0.32em] text-foreground/85">
{cardholderName}
</p>
</div>
<div className="space-y-1 text-right">
<span className="text-[10px] uppercase tracking-[0.36em] text-muted-foreground/70">
Expires
</span>
<p className="text-sm font-semibold text-foreground/85">
{expiryDate}
</p>
</div>
</div>
</motion.div>
{/* Back */}
<motion.div
className="absolute inset-0 flex flex-col justify-between rounded-[1.5rem] p-6 shadow-[0_22px_55px_-30px_rgba(15,23,42,0.75)]"
style={{
background: gradients.back,
backfaceVisibility: "hidden",
WebkitBackfaceVisibility: "hidden",
rotateY: 180,
}}
animate={{ rotateY: isFlipped ? 0 : 180 }}
transition={{ duration: 0.55, ease: "easeInOut" }}
>
<div
aria-hidden
className="pointer-events-none absolute inset-0 rounded-[1.5rem]"
style={{
background: overlayGradient,
opacity: shouldReduceMotion ? 0.5 : 0.9,
}}
/>
<div
className="absolute left-0 right-0 top-8 h-12 rounded-sm bg-foreground/10"
aria-hidden
/>
<div className="relative mt-16 space-y-3">
<div className="flex items-center gap-2">
<span className="text-[10px] uppercase tracking-[0.32em] text-muted-foreground/70">
CVV
</span>
<span className="flex min-w-[80px] items-center justify-center rounded-md border border-border/50 bg-muted px-3 py-1 font-mono text-lg font-semibold text-foreground shadow-[0_6px_18px_-12px_rgba(15,23,42,0.6)]">
{cvv}
</span>
</div>
<p className="text-[10px] uppercase tracking-[0.32em] text-muted-foreground/60">
Contact support if you notice unauthorized activity.
</p>
</div>
<div className="relative flex items-center justify-end">
<CreditCardIcon
className="h-6 w-6 text-muted-foreground/70"
aria-hidden
/>
</div>
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,457 @@
"use client";
import { NativeButton } from "@/components/native/baseui/native-button-baseui";
import { AnimatePresence, motion } from "framer-motion";
import {
Bold,
ChevronDown,
Italic,
List,
ListOrdered,
Plus,
RotateCcw,
Save,
Underline,
X,
} from "lucide-react";
import { useEffect, useMemo, useRef, useState } from "react";
type Priority = "high" | "medium" | "low";
type TeamMember = {
id: string;
name: string;
role: string;
initials: string;
accent: string;
};
const allMembers: TeamMember[] = [
{
id: "sophia",
name: "Sophia Williams",
role: "Product Designer",
initials: "SW",
accent: "ring-foreground text-foreground",
},
{
id: "liam",
name: "Liam Johnson",
role: "Design Manager",
initials: "LJ",
accent: "ring-foreground text-foreground",
},
{
id: "olivia",
name: "Olivia Smith",
role: "UX Researcher",
initials: "OS",
accent: "ring-foreground text-foreground",
},
{
id: "mia",
name: "Mia Chen",
role: "Product Owner",
initials: "MC",
accent: "ring-foreground text-foreground",
},
{
id: "ethan",
name: "Ethan Davis",
role: "UI Engineer",
initials: "ED",
accent: "ring-foreground text-foreground",
},
];
const priorityMap: Record<
Priority,
{ label: string; badge: string; dot: string; description: string }
> = {
high: {
label: "High",
badge:
"border border-destructive/40 bg-destructive/20 text-destructive dark:text-red-400",
dot: "bg-destructive",
description: "Requires immediate focus and dedicated resources",
},
medium: {
label: "Medium",
badge:
"border border-amber-500/30 bg-amber-500/20 text-amber-600 dark:text-amber-400",
dot: "bg-amber-500",
description: "Important but not blocking other work",
},
low: {
label: "Low",
badge:
"border border-emerald-500/30 bg-emerald-500/20 text-emerald-600 dark:text-emerald-400",
dot: "bg-emerald-500",
description: "Nice-to-have improvements to schedule later",
},
};
const defaultDescription =
"The goal is to update the current design system with the latest components and styles. This includes reviewing existing elements, identifying areas for improvement, and implementing changes to ensure consistency and usability across all platforms.";
const maxDescriptionLength = 200;
export function DetailTaskCardBaseUI() {
const [title, setTitle] = useState("Edit Design System");
const [priority, setPriority] = useState<Priority>("high");
const [assignees, setAssignees] = useState<TeamMember[]>(
allMembers.slice(0, 3)
);
const [description, setDescription] = useState(defaultDescription);
const [reminderEnabled, setReminderEnabled] = useState(true);
const [isSaving, setIsSaving] = useState(false);
const [isSaved, setIsSaved] = useState(false);
const [dropdownOpen, setDropdownOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const remainingCharacters = maxDescriptionLength - description.length;
const availableMembers = useMemo(
() =>
allMembers.filter(
(member) => !assignees.some((assigned) => assigned.id === member.id)
),
[assignees]
);
const handleRemoveAssignee = (id: string) => {
setAssignees((prev) => prev.filter((member) => member.id !== id));
};
const handleAddPerson = () => {
if (availableMembers.length === 0) return;
const [nextMember] = availableMembers;
setAssignees((prev) => [...prev, nextMember]);
};
const handleReset = () => {
setTitle("Edit Design System");
setPriority("high");
setAssignees(allMembers.slice(0, 3));
setDescription(defaultDescription);
setReminderEnabled(true);
setIsSaved(false);
};
const handleSave = () => {
if (isSaving) return;
setIsSaving(true);
setIsSaved(false);
setTimeout(() => {
setIsSaving(false);
setIsSaved(true);
setTimeout(() => setIsSaved(false), 2000);
}, 900);
};
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setDropdownOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const toolbarIcons = [Bold, Italic, Underline, List, ListOrdered];
return (
<div className="">
{/* Card replacement */}
<div className="group relative w-full overflow-hidden rounded-2xl border border-border/40 bg-background/60 text-foreground backdrop-blur transition-all hover:border-border/60 hover:shadow-lg">
<div className="pointer-events-none absolute inset-0 bg-gradient-to-br from-foreground/[0.04] via-transparent to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100 -z-10" />
{/* CardHeader replacement */}
<div className="relative flex flex-col gap-3 border-b border-border/40 bg-background/40 px-6 py-6">
{/* Badge replacement */}
<span className="w-fit rounded-full bg-primary/15 px-3 py-1 text-[0.65rem] font-medium uppercase tracking-[0.25em] text-primary transition-colors hover:bg-primary hover:text-primary-foreground">
Task Manager
</span>
{/* CardTitle replacement */}
<h3 className="text-sm font-semibold uppercase tracking-[0.25em] text-foreground">
Detail Task Overview
</h3>
{/* CardDescription replacement */}
<p className="text-sm text-foreground/70">
Keep your task aligned with team priorities and deliverables.
</p>
</div>
{/* CardContent replacement */}
<div className="space-y-10 px-6 py-8">
<motion.div
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35, ease: "easeOut" }}
className="grid gap-6 md:grid-cols-2"
>
<div className="space-y-3">
<label
htmlFor="task-title"
className="text-xs font-semibold uppercase tracking-[0.2em] text-foreground/60"
>
Title Task
</label>
{/* Input replacement */}
<input
id="task-title"
type="text"
value={title}
onChange={(event) => setTitle(event.target.value)}
className="flex h-10 w-full rounded-xl border border-border/40 bg-background/40 px-3 py-2 text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:border-border/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40"
aria-describedby="task-title-description"
/>
<p
id="task-title-description"
className="text-xs text-foreground/60"
>
Keep it short and goal oriented.
</p>
</div>
<div className="space-y-3">
<span className="text-xs font-semibold uppercase tracking-[0.2em] text-foreground/60">
Priority
</span>
{/* DropdownMenu replacement */}
<div className="relative" ref={dropdownRef}>
<button
type="button"
onClick={() => setDropdownOpen(!dropdownOpen)}
className="flex w-full items-center justify-between gap-3 rounded-xl border border-border/40 bg-background/40 px-3 py-2 text-sm font-medium text-foreground transition-all hover:border-border/60 hover:bg-background/60"
>
<span className="flex items-center gap-3">
<span
className={`h-2.5 w-2.5 rounded-full ${priorityMap[priority].dot}`}
aria-hidden="true"
/>
<span>{priorityMap[priority].label}</span>
</span>
<ChevronDown
className="h-4 w-4 text-foreground/60"
aria-hidden="true"
/>
</button>
{dropdownOpen && (
<div className="absolute right-0 z-50 mt-2 w-44 rounded-xl border border-border/40 bg-background/70 p-1 backdrop-blur shadow-lg">
{(Object.keys(priorityMap) as Priority[]).map((option) => (
<button
key={option}
onClick={() => {
setPriority(option);
setDropdownOpen(false);
}}
className="flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-sm text-foreground/80 transition-colors hover:bg-background/60 hover:text-foreground"
>
<span className="flex items-center gap-2">
<span
className={`h-2.5 w-2.5 rounded-full ${priorityMap[option].dot}`}
aria-hidden="true"
/>
{priorityMap[option].label}
</span>
{priority === option ? (
<span
className={`rounded-full px-2 py-0.5 text-[0.65rem] font-medium uppercase tracking-[0.15em] ${priorityMap[option].badge}`}
>
Selected
</span>
) : null}
</button>
))}
</div>
)}
</div>
<p className="text-xs text-foreground/60">
{priorityMap[priority].description}
</p>
</div>
</motion.div>
<div className="space-y-4">
<div className="flex items-center justify-between">
<span className="text-xs font-semibold uppercase tracking-[0.2em] text-foreground/60">
Assign Task To
</span>
<span className="rounded-full border border-border/40 bg-background/50 px-3 py-1 text-[0.65rem] font-medium uppercase tracking-[0.25em] text-foreground/70 backdrop-blur transition-colors hover:border-border/60 hover:bg-background/70 hover:text-foreground">
Team
</span>
</div>
<div className="flex flex-wrap gap-3">
<AnimatePresence>
{assignees.map((member) => (
<motion.div
layout
key={member.id}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: -8 }}
transition={{ duration: 0.2 }}
className="flex items-center gap-3 rounded-xl border border-border/40 bg-background/40 px-3 py-2 backdrop-blur transition-colors hover:border-border/60"
>
<span
className={`flex h-8 w-8 items-center justify-center rounded-full bg-background/70 text-xs font-semibold tracking-tight ring-1 ring-border/40 ring-offset-2 ring-offset-background ${member.accent}`}
>
{member.initials}
</span>
<div className="flex flex-col text-left">
<span className="text-sm font-medium text-foreground">
{member.name}
</span>
<span className="text-xs text-foreground/60">
{member.role}
</span>
</div>
{/* Button replacement */}
<button
type="button"
onClick={() => handleRemoveAssignee(member.id)}
className="inline-flex h-8 w-8 items-center justify-center rounded-lg text-foreground/60 transition-colors hover:bg-background/60 hover:text-foreground"
aria-label={`Remove ${member.name} from this task`}
>
<X className="h-4 w-4" />
</button>
</motion.div>
))}
</AnimatePresence>
<button
type="button"
onClick={handleAddPerson}
disabled={availableMembers.length === 0}
className="flex items-center gap-2 rounded-xl border border-dashed border-border/50 bg-transparent px-3 py-2 text-sm text-foreground/80 transition-all hover:border-border/70 hover:bg-background/40 disabled:cursor-not-allowed disabled:opacity-70"
>
<Plus className="h-4 w-4" />
Add another person
</button>
</div>
</div>
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-xs font-semibold uppercase tracking-[0.2em] text-foreground/60">
Description
</span>
<span className="text-xs text-foreground/60">
{Math.max(0, remainingCharacters)} / {maxDescriptionLength}
</span>
</div>
<div className="rounded-2xl border border-border/40 bg-background/40 backdrop-blur">
<div className="flex items-center gap-1 border-b border-border/40 px-3 py-2 text-foreground/60">
{toolbarIcons.map((Icon, index) => (
<button
key={Icon.displayName ?? index}
type="button"
className="inline-flex h-9 w-9 items-center justify-center rounded-lg text-foreground/60 transition-colors hover:bg-background/60 hover:text-foreground"
aria-label={`Formatting option ${Icon.displayName ?? index + 1}`}
>
<Icon className="h-4 w-4" />
</button>
))}
</div>
{/* Textarea replacement */}
<textarea
value={description}
onChange={(event) =>
setDescription(
event.target.value.slice(0, maxDescriptionLength)
)
}
className="h-32 w-full resize-none border-0 bg-transparent px-3 py-3 text-sm text-foreground/80 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0"
aria-label="Task description"
/>
</div>
</div>
<div className="flex flex-col gap-4 rounded-2xl border border-border/40 bg-background/40 px-4 py-4 backdrop-blur md:flex-row md:items-center md:justify-between">
<div className="flex items-center gap-3">
<motion.button
type="button"
role="switch"
aria-label="Toggle reminder task"
aria-checked={reminderEnabled}
onClick={() => setReminderEnabled((prev) => !prev)}
className={`relative flex h-6 w-12 items-center rounded-full border border-border/50 transition-all ${
reminderEnabled ? "bg-primary/20" : "bg-background/60"
}`}
>
<motion.span
layout
className="absolute left-1 top-1 h-4 w-4 rounded-full bg-primary shadow-lg"
animate={{ x: reminderEnabled ? 22 : 0 }}
transition={{ type: "spring", stiffness: 400, damping: 32 }}
/>
</motion.button>
<div>
<p className="text-sm font-medium text-foreground">
Reminder Task
</p>
<p className="text-xs text-foreground/60">
{reminderEnabled
? "We will notify the assignees 24 hours before the due date."
: "Enable reminders to keep everyone on track."}
</p>
</div>
</div>
<span className="rounded-full border border-border/40 bg-background/60 px-3 py-1 text-[0.65rem] font-medium uppercase tracking-[0.25em] text-foreground/70 backdrop-blur transition-colors hover:border-border/60 hover:bg-background/70 hover:text-foreground">
Sprint Q4
</span>
</div>
</div>
{/* CardFooter replacement */}
<div className="flex flex-col gap-4 border-t border-border/40 bg-background/50 px-6 py-5 backdrop-blur sm:flex-row sm:items-center sm:justify-between">
<div className="flex items-center gap-2 text-sm text-foreground/60">
<RotateCcw className="h-4 w-4" aria-hidden="true" />
<span>Need to start over?</span>
<button
type="button"
className="px-0 text-sm text-foreground underline-offset-4 hover:text-primary hover:underline"
onClick={handleReset}
>
Reset
</button>
</div>
<div className="flex items-center gap-3">
<AnimatePresence mode="popLayout" initial={false}>
{isSaved ? (
<motion.span
key="saved"
initial={{ opacity: 0, y: 6 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -6 }}
transition={{ duration: 0.2 }}
className="text-sm text-emerald-600 dark:text-emerald-400"
>
Saved!
</motion.span>
) : null}
</AnimatePresence>
<NativeButton
variant="default"
size="default"
onClick={handleSave}
disabled={isSaving}
loading={isSaving}
>
<Save className="h-4 w-4" aria-hidden="true" />
{isSaving ? "Saving..." : "Save Changes"}
</NativeButton>
</div>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,235 @@
"use client";
import { NativeButton } from "@/components/native/baseui/native-button-baseui";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { Check, ShieldCheck, Sparkles, Star, Truck } from "lucide-react";
import { useState } from "react";
type Plan = {
id: string;
title: string;
price: string;
description: string;
promise: string;
};
const plans: Plan[] = [
{
id: "essential",
title: "Essential",
price: "$48.00",
description:
"Everyday comfort with signature cushioning and durable materials.",
promise: "Ships in 2 business days • 30-day returns",
},
{
id: "plus",
title: "Plus",
price: "$68.00",
description:
"Upgraded foam footbed, recycled laces, and breathable knit upper.",
promise: "Ships next business day • 45-day returns",
},
{
id: "premium",
title: "Premium",
price: "$92.00",
description:
"Adaptive arch support and antimicrobial lining for long days on your feet.",
promise: "Priority fulfillment • 60-day returns",
},
];
const highlights = [
{
icon: Sparkles,
label: "Best Seller",
},
{
icon: ShieldCheck,
label: "2-year warranty",
},
];
export function EcommerceHighlightCardBaseUI() {
const [activePlan, setActivePlan] = useState<Plan>(plans[1]);
const shouldReduceMotion = useReducedMotion();
return (
<div className="w-full">
<motion.div
initial={shouldReduceMotion ? false : { opacity: 0, y: 24 }}
animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }}
transition={
shouldReduceMotion ? undefined : { duration: 0.45, ease: "easeOut" }
}
className="relative z-10"
>
{/* Card replacement */}
<div className="group relative overflow-hidden rounded-[28px] border border-border/40 bg-background text-foreground">
<div
aria-hidden="true"
className="absolute inset-0 bg-gradient-to-br from-foreground/[0.04] via-transparent to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100 -z-10"
/>
{/* CardHeader replacement */}
<div className="relative flex flex-col space-y-1.5 p-6 pb-0">
{/* Badge replacement */}
<span className="w-fit rounded-full bg-primary/15 px-3 py-1 text-[0.65rem] font-medium uppercase tracking-[0.25em] text-primary transition-colors hover:bg-primary hover:text-primary-foreground">
Nimbus Collection
</span>
<div className="space-y-2">
{/* CardTitle replacement */}
<h3 className="text-2xl font-semibold tracking-tight">
Nimbus Pace Runner
</h3>
{/* CardDescription replacement */}
<p className="text-sm text-muted-foreground">
Lightweight performance kicks engineered for all-day comfort and
momentum.
</p>
</div>
<div className="flex items-center gap-3 text-sm">
<div className="flex items-center gap-1 text-primary">
{[0, 1, 2, 3, 4].map((star) => (
<Star
key={star}
className="h-4 w-4 fill-current"
aria-hidden="true"
/>
))}
</div>
<span className="text-muted-foreground">4.9 1,240 reviews</span>
</div>
</div>
{/* CardContent replacement */}
<div className="relative space-y-6 p-6 pt-6">
<div className="flex flex-wrap gap-2">
{highlights.map(({ icon: Icon, label }) => (
<span
key={label}
className="inline-flex items-center gap-2 rounded-full border border-border/70 bg-muted/60 px-3 py-1 text-xs font-medium text-muted-foreground backdrop-blur"
>
<Icon
className="h-3.5 w-3.5 text-primary"
aria-hidden="true"
/>
{label}
</span>
))}
</div>
<div className="grid gap-2">
<span className="text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">
Choose your bundle
</span>
<div className="flex flex-wrap gap-2">
{plans.map((plan) => {
const isActive = plan.id === activePlan.id;
return (
/* Button replacement */
<button
key={plan.id}
type="button"
onClick={() => setActivePlan(plan)}
aria-pressed={isActive}
className={`inline-flex items-center justify-center rounded-full border border-border/60 px-4 py-2 text-sm font-medium transition-all ${
isActive
? "border-primary/40 bg-primary text-primary-foreground shadow-lg"
: "bg-background/80 text-foreground hover:border-primary/30 hover:bg-muted"
}`}
>
{plan.title}
</button>
);
})}
</div>
</div>
<AnimatePresence mode="wait">
<motion.div
key={activePlan.id}
initial={shouldReduceMotion ? false : { opacity: 0, y: 12 }}
animate={
shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }
}
exit={
shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: -10 }
}
transition={
shouldReduceMotion
? undefined
: {
duration: 0.32,
ease: "easeOut",
opacity: { duration: 0.25 },
}
}
className="space-y-3 rounded-2xl border border-border/60 bg-card/60 p-4 shadow-sm backdrop-blur"
>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-muted-foreground">
{activePlan.title}
</p>
<p className="text-base font-semibold text-foreground">
{activePlan.price}
</p>
</div>
<span className="inline-flex items-center gap-1 rounded-full bg-primary/15 px-3 py-1 text-xs font-medium uppercase tracking-[0.2em] text-primary">
<Check className="h-3.5 w-3.5" aria-hidden="true" />
Selected
</span>
</div>
<p className="text-sm leading-relaxed text-muted-foreground">
{activePlan.description}
</p>
<p className="text-xs font-medium text-foreground">
{activePlan.promise}
</p>
</motion.div>
</AnimatePresence>
{/* Separator replacement */}
<div className="h-px w-full bg-border/60" />
<div className="flex flex-col gap-3 rounded-2xl border border-border/60 bg-muted/50 p-4 text-sm text-muted-foreground backdrop-blur-sm">
<div className="flex items-center gap-2">
<Truck className="h-4 w-4 text-primary" aria-hidden="true" />
<span>Free express shipping on orders over $100</span>
</div>
<div className="flex items-center gap-2">
<ShieldCheck
className="h-4 w-4 text-primary"
aria-hidden="true"
/>
<span>Extended warranty included with every bundle</span>
</div>
</div>
</div>
{/* CardFooter replacement */}
<div className="relative flex flex-col gap-4 border-t border-border/50 bg-muted/40 px-8 py-6 sm:flex-row sm:items-center sm:justify-between">
<div className="text-sm">
<p className="font-medium text-foreground">
Arrives before Friday
</p>
<p className="text-xs text-muted-foreground">
Checkout before 2PM local time
</p>
</div>
<NativeButton
variant="default"
size="lg"
glow
className="w-full sm:w-auto"
>
Add to bag {activePlan.price}
</NativeButton>
</div>
</div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,130 @@
"use client";
import { NativeButton } from "@/components/native/baseui/native-button-baseui";
import { cn } from "@/lib/utils";
import { Avatar } from "@base-ui/react/avatar";
import { motion } from "framer-motion";
import { BookOpen, Clock } from "lucide-react";
interface GlassBlogCardBaseUIProps {
title?: string;
excerpt?: string;
image?: string;
author?: {
name: string;
avatar: string;
};
date?: string;
readTime?: string;
tags?: string[];
className?: string;
}
const defaultPost = {
title: "The Future of UI Design",
excerpt:
"Exploring the latest trends in glassmorphism, 3D elements, and micro-interactions.",
image:
"https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=800&q=80",
author: {
name: "Moumen Soliman",
avatar: "https://iimydr2b8o.ufs.sh/f/Zqn6AViLMoTtoUjLg4dAryGEidskK72wBCQA6DNcZH4Xh5b8",
},
date: "Dec 2, 2025",
readTime: "5 min read",
tags: ["Design", "UI/UX"],
};
const getInitials = (name: string) =>
name
.split(" ")
.map((n) => n[0])
.join("")
.toUpperCase()
.slice(0, 2);
export function GlassBlogCardBaseUI({
title = defaultPost.title,
excerpt = defaultPost.excerpt,
image = defaultPost.image,
author = defaultPost.author,
date = defaultPost.date,
readTime = defaultPost.readTime,
tags = defaultPost.tags,
className,
}: GlassBlogCardBaseUIProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
className={cn("w-full max-w-[400px]", className)}
>
<div className="group relative h-full overflow-hidden rounded-2xl border border-border/50 bg-card/30 backdrop-blur-md transition-all duration-300 hover:border-primary/50 hover:shadow-xl hover:shadow-primary/10">
{/* Image Section */}
<div className="relative aspect-[16/9] overflow-hidden">
<motion.img
src={image}
alt={title}
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-t from-background/80 to-transparent opacity-60 transition-opacity duration-300 group-hover:opacity-40" />
<div className="absolute bottom-3 left-3 flex gap-2">
{tags?.map((tag, index) => (
<span
key={index}
className="inline-flex items-center rounded-md bg-secondary/80 px-2.5 py-0.5 text-xs font-medium text-secondary-foreground backdrop-blur-sm hover:bg-secondary"
>
{tag}
</span>
))}
</div>
{/* Hover Overlay Action */}
<div className="absolute inset-0 flex items-center justify-center bg-background/20 backdrop-blur-[2px] opacity-0 transition-opacity duration-300 group-hover:opacity-100">
<NativeButton variant="default" size="default" glow>
<BookOpen className="h-4 w-4" />
Read Article
</NativeButton>
</div>
</div>
{/* Content Section */}
<div className="flex flex-col gap-4 p-5">
<div className="space-y-2">
<h3 className="text-xl font-semibold leading-tight tracking-tight text-foreground transition-colors group-hover:text-primary">
{title}
</h3>
<p className="line-clamp-2 text-sm text-muted-foreground">
{excerpt}
</p>
</div>
<div className="flex items-center justify-between border-t border-border/50 pt-4">
<div className="flex items-center gap-2">
{/* BaseUI Avatar */}
<Avatar.Root className="relative flex h-8 w-8 shrink-0 overflow-hidden rounded-full border border-border/50">
<Avatar.Image src={author.avatar} alt={author.name} />
<Avatar.Fallback className="flex h-full w-full items-center justify-center rounded-full bg-muted text-xs font-medium text-muted-foreground">
{getInitials(author.name)}
</Avatar.Fallback>
</Avatar.Root>
<div className="flex flex-col text-xs">
<span className="font-medium text-foreground">
{author.name}
</span>
<span className="text-muted-foreground">{date}</span>
</div>
</div>
<div className="flex items-center gap-1 text-xs text-muted-foreground">
<Clock className="h-3 w-3" />
<span>{readTime}</span>
</div>
</div>
</div>
</div>
</motion.div>
);
}

View file

@ -0,0 +1,153 @@
"use client";
import { NativeButton } from "@/components/native/baseui/native-button-baseui";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import { Calendar, CreditCard, Lock } from "lucide-react";
import { useState } from "react";
interface GlassCheckoutCardBaseUIProps {
amount?: number;
className?: string;
}
export function GlassCheckoutCardBaseUI({
amount = 85.8,
className,
}: GlassCheckoutCardBaseUIProps) {
const [paymentMethod, setPaymentMethod] = useState("card");
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
className={cn("w-full max-w-[400px]", className)}
>
{/* Card replacement */}
<div className="group relative overflow-hidden rounded-2xl border border-border/50 bg-card/30 backdrop-blur-md transition-all duration-300 hover:border-primary/50 hover:shadow-xl hover:shadow-primary/10">
<div className="p-6">
<div className="mb-6">
<h3 className="text-lg font-semibold text-foreground">
Payment Details
</h3>
<p className="text-sm text-muted-foreground">
Complete your purchase securely
</p>
</div>
{/* Payment Methods */}
<div className="mb-6 grid grid-cols-3 gap-2">
{["card", "paypal", "apple"].map((method) => (
<button
key={method}
onClick={() => setPaymentMethod(method)}
className={cn(
"flex h-12 items-center justify-center rounded-lg border border-border/50 bg-background/50 transition-all hover:bg-background/80",
paymentMethod === method &&
"border-primary bg-primary/10 text-primary"
)}
>
{method === "card" && <CreditCard className="h-5 w-5" />}
{method === "paypal" && (
<span className="font-bold italic">Pay</span>
)}
{method === "apple" && (
<span className="font-semibold">Pay</span>
)}
</button>
))}
</div>
<div className="space-y-4">
<div className="space-y-2">
{/* Label replacement */}
<label
htmlFor="cardNumber"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Card Number
</label>
<div className="relative">
{/* Input replacement */}
<input
id="cardNumber"
type="text"
placeholder="0000 0000 0000 0000"
className="flex h-10 w-full rounded-md border border-border/50 bg-background/50 px-3 py-2 pl-10 text-sm backdrop-blur-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus:border-primary/50 focus:bg-background/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
<CreditCard className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" />
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label
htmlFor="expiry"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Expiry Date
</label>
<div className="relative">
<input
id="expiry"
type="text"
placeholder="MM/YY"
className="flex h-10 w-full rounded-md border border-border/50 bg-background/50 px-3 py-2 pl-10 text-sm backdrop-blur-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus:border-primary/50 focus:bg-background/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
<Calendar className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" />
</div>
</div>
<div className="space-y-2">
<label
htmlFor="cvc"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
CVC
</label>
<div className="relative">
<input
id="cvc"
type="text"
placeholder="123"
className="flex h-10 w-full rounded-md border border-border/50 bg-background/50 px-3 py-2 pl-10 text-sm backdrop-blur-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus:border-primary/50 focus:bg-background/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
<Lock className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" />
</div>
</div>
</div>
<div className="space-y-2">
<label
htmlFor="name"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Cardholder Name
</label>
<input
id="name"
type="text"
placeholder="John Doe"
className="flex h-10 w-full rounded-md border border-border/50 bg-background/50 px-3 py-2 text-sm backdrop-blur-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus:border-primary/50 focus:bg-background/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
/>
</div>
</div>
<NativeButton
variant="default"
size="lg"
glow
className="mt-6 w-full"
>
Pay ${amount.toFixed(2)}
</NativeButton>
<p className="mt-4 text-center text-xs text-muted-foreground">
<Lock className="inline-block h-3 w-3 mr-1" />
Payments are secure and encrypted
</p>
</div>
</div>
</motion.div>
);
}

View file

@ -0,0 +1,147 @@
"use client";
import { NativeButton } from "@/components/native/baseui/native-button-baseui";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import { ArrowRight, CreditCard, ShoppingBag } from "lucide-react";
interface OrderItem {
id: string;
name: string;
price: number;
image: string;
quantity: number;
variant?: string;
}
interface GlassOrderSummaryBaseUIProps {
items?: OrderItem[];
subtotal?: number;
tax?: number;
shipping?: number;
total?: number;
className?: string;
}
const defaultItems: OrderItem[] = [
{
id: "1",
name: "Premium Icon Pack",
price: 29.0,
image:
"https://images.unsplash.com/photo-1611162617474-5b21e879e113?w=100&q=80",
quantity: 1,
variant: "Pro License",
},
{
id: "2",
name: "UI Design Kit",
price: 49.0,
image:
"https://images.unsplash.com/photo-1586717791821-3f44a5638d48?w=100&q=80",
quantity: 1,
variant: "Dark Mode",
},
];
export function GlassOrderSummaryBaseUI({
items = defaultItems,
subtotal = 78.0,
tax = 7.8,
shipping = 0,
total = 85.8,
className,
}: GlassOrderSummaryBaseUIProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
className={cn("w-full max-w-[400px]", className)}
>
{/* Card replacement */}
<div className="group relative overflow-hidden rounded-2xl border border-border/50 bg-card/30 backdrop-blur-md transition-all duration-300 hover:border-primary/50 hover:shadow-xl hover:shadow-primary/10">
<div className="p-6">
<div className="mb-6 flex items-center gap-2">
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/10 text-primary">
<ShoppingBag className="h-4 w-4" />
</div>
<h3 className="text-lg font-semibold text-foreground">
Order Summary
</h3>
</div>
<div className="space-y-4">
{items.map((item) => (
<motion.div
key={item.id}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
className="flex items-center gap-4 rounded-lg p-2 transition-colors hover:bg-background/40"
>
<div className="h-12 w-12 overflow-hidden rounded-md border border-border/50 bg-background/50">
<img
src={item.image}
alt={item.name}
className="h-full w-full object-cover"
/>
</div>
<div className="flex-1">
<h4 className="text-sm font-medium text-foreground">
{item.name}
</h4>
<p className="text-xs text-muted-foreground">
{item.variant}
</p>
</div>
<div className="text-right">
<p className="text-sm font-medium text-foreground">
${item.price.toFixed(2)}
</p>
<p className="text-xs text-muted-foreground">
x{item.quantity}
</p>
</div>
</motion.div>
))}
</div>
{/* Separator replacement */}
<div className="my-6 h-px w-full bg-border/50" />
<div className="space-y-2 text-sm">
<div className="flex justify-between text-muted-foreground">
<span>Subtotal</span>
<span>${subtotal.toFixed(2)}</span>
</div>
<div className="flex justify-between text-muted-foreground">
<span>Tax</span>
<span>${tax.toFixed(2)}</span>
</div>
{shipping > 0 && (
<div className="flex justify-between text-muted-foreground">
<span>Shipping</span>
<span>${shipping.toFixed(2)}</span>
</div>
)}
<div className="mt-4 flex justify-between text-base font-semibold text-foreground">
<span>Total</span>
<span>${total.toFixed(2)}</span>
</div>
</div>
<NativeButton
variant="default"
size="lg"
glow
className="mt-6 w-full"
>
<CreditCard className="h-4 w-4" />
Pay Now
<ArrowRight className="h-4 w-4" />
</NativeButton>
</div>
</div>
</motion.div>
);
}

View file

@ -0,0 +1,142 @@
"use client";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import {
ArrowDownLeft,
ArrowUpRight,
CreditCard,
TrendingUp,
Wallet,
} from "lucide-react";
interface GlassWalletCardBaseUIProps {
balance?: string;
currency?: string;
address?: string;
trend?: string;
trendUp?: boolean;
cardHolder?: string;
expiry?: string;
className?: string;
}
const defaultWallet = {
balance: "12,345.67",
currency: "ETH",
address: "0x71C...9A23",
trend: "+5.2%",
trendUp: true,
cardHolder: "Alex Morgan",
expiry: "12/28",
};
export function GlassWalletCardBaseUI({
balance = defaultWallet.balance,
currency = defaultWallet.currency,
address = defaultWallet.address,
trend = defaultWallet.trend,
trendUp = defaultWallet.trendUp,
cardHolder = defaultWallet.cardHolder,
expiry = defaultWallet.expiry,
className,
}: GlassWalletCardBaseUIProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
className={cn("w-full max-w-[400px]", className)}
>
{/* Card replacement */}
<div className="group relative h-56 overflow-hidden rounded-2xl border border-border/50 bg-gradient-to-br from-card/80 via-card/40 to-card/20 backdrop-blur-md transition-all duration-300 hover:border-primary/50 hover:shadow-xl hover:shadow-primary/10">
{/* Abstract Background Shapes */}
<div className="absolute -right-16 -top-16 h-48 w-48 rounded-full bg-primary/10 blur-3xl transition-all duration-500 group-hover:bg-primary/20" />
<div className="absolute -bottom-16 -left-16 h-48 w-48 rounded-full bg-secondary/10 blur-3xl transition-all duration-500 group-hover:bg-secondary/20" />
<div className="relative flex h-full flex-col justify-between p-6">
{/* Header */}
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary backdrop-blur-sm">
<Wallet className="h-5 w-5" />
</div>
<div>
<p className="text-xs font-medium text-muted-foreground">
Total Balance
</p>
<div className="flex items-baseline gap-1">
<h3 className="text-2xl font-bold tracking-tight text-foreground">
${balance}
</h3>
<span className="text-xs font-medium text-muted-foreground">
{currency}
</span>
</div>
</div>
</div>
{/* Badge replacement */}
<span
className={cn(
"inline-flex items-center rounded-full border border-border/50 bg-background/50 px-2.5 py-0.5 text-xs font-medium backdrop-blur-sm",
trendUp ? "text-green-500" : "text-red-500"
)}
>
<TrendingUp className="mr-1 h-3 w-3" />
{trend}
</span>
</div>
{/* Card Details */}
<div className="space-y-4">
<div className="flex items-center justify-between text-sm">
<div className="flex items-center gap-2 text-muted-foreground">
<CreditCard className="h-4 w-4" />
<span> 4242</span>
</div>
<span className="font-mono text-xs text-muted-foreground">
{expiry}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-foreground">
{cardHolder}
</span>
<span className="rounded-full bg-secondary/50 px-3 py-1 font-mono text-xs text-secondary-foreground backdrop-blur-sm">
{address}
</span>
</div>
</div>
{/* Hover Actions Overlay */}
<div className="absolute inset-0 flex items-center justify-center gap-6 bg-background/60 backdrop-blur-[2px] opacity-0 transition-opacity duration-300 group-hover:opacity-100">
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="flex flex-col items-center gap-2"
>
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg shadow-primary/25">
<ArrowUpRight className="h-6 w-6" />
</div>
<span className="text-sm font-medium text-foreground">Send</span>
</motion.button>
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="flex flex-col items-center gap-2"
>
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-secondary text-secondary-foreground shadow-lg">
<ArrowDownLeft className="h-6 w-6" />
</div>
<span className="text-sm font-medium text-foreground">
Receive
</span>
</motion.button>
</div>
</div>
</div>
</motion.div>
);
}

View file

@ -0,0 +1,40 @@
"use client";
import { motion, useReducedMotion } from "framer-motion";
export function HoverExpandCardBaseUI() {
const shouldReduceMotion = useReducedMotion();
return (
<div className="">
<motion.div
whileHover={shouldReduceMotion ? undefined : { y: -10, scale: 1.015 }}
whileFocus={shouldReduceMotion ? undefined : { y: -10, scale: 1.015 }}
transition={{ type: "spring", stiffness: 260, damping: 26 }}
className="group rounded-3xl border border-border/60 bg-card/80 p-6 backdrop-blur-xl transition-shadow duration-300 hover:shadow-[0_28px_90px_-40px_rgba(15,23,42,0.75)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
tabIndex={0}
role="group"
aria-label="Hover expand card demonstrating glassmorphic elevation."
>
<div className="relative mb-4 h-40 overflow-hidden rounded-2xl border border-border/60 bg-gradient-to-br from-foreground/10 via-background/40 to-transparent">
<motion.div
aria-hidden
className="absolute inset-0 bg-[radial-gradient(circle_at_30%_20%,rgba(255,255,255,0.4),transparent_55%),radial-gradient(circle_at_80%_0%,rgba(79,70,229,0.45),transparent_60%)] transition-transform duration-500"
whileHover={shouldReduceMotion ? undefined : { scale: 1.05 }}
/>
</div>
{/* Badge replacement */}
<span className="mb-3 inline-flex w-fit rounded-full border border-border/60 bg-white/5 px-3 py-1 text-[10px] uppercase tracking-[0.3em] text-muted-foreground">
Feature
</span>
<h3 className="mb-2 text-xl font-semibold text-foreground">
Beautiful Card
</h3>
<p className="text-sm leading-relaxed text-muted-foreground">
Hover or focus to gently lift and expand the surface. Animations stay
calm but responsive, matching the glassmorphic system.
</p>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,102 @@
"use client";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import { ExternalLink, Github } from "lucide-react";
interface ProjectCardBaseUIProps {
title?: string;
description?: string;
tags?: string[];
image?: string;
links?: {
demo?: string;
github?: string;
};
className?: string;
}
const defaultProject = {
title: "E-Commerce Platform",
description:
"Full-stack online store with payment integration and inventory management",
tags: ["React", "Node.js", "PostgreSQL", "Stripe"],
image:
"https://images.unsplash.com/photo-1661956602116-aa6865609028?w=800&q=80",
links: { demo: "#", github: "#" },
};
export function ProjectCardBaseUI({
title = defaultProject.title,
description = defaultProject.description,
tags = defaultProject.tags,
image = defaultProject.image,
links = defaultProject.links,
className,
}: ProjectCardBaseUIProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
className={cn("w-full max-w-[400px]", className)}
>
{/* Card replacement */}
<div className="group relative h-full overflow-hidden rounded-2xl border border-border/50 bg-card/50 backdrop-blur-sm transition-all duration-300 hover:border-primary/50 hover:shadow-xl hover:shadow-primary/10">
<div className="relative aspect-video overflow-hidden">
<motion.img
src={image}
alt={title}
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-t from-background/90 via-background/20 to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
<div className="absolute inset-0 flex items-center justify-center gap-4 opacity-0 transition-all duration-300 group-hover:opacity-100">
{links?.demo && (
<motion.a
href={links.demo}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
className="flex h-10 w-10 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg shadow-primary/25 backdrop-blur-md"
title="View Demo"
>
<ExternalLink className="h-5 w-5" />
</motion.a>
)}
{links?.github && (
<motion.a
href={links.github}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
className="flex h-10 w-10 items-center justify-center rounded-full bg-secondary text-secondary-foreground shadow-lg backdrop-blur-md"
title="View Code"
>
<Github className="h-5 w-5" />
</motion.a>
)}
</div>
</div>
<div className="p-5">
<h3 className="mb-2 text-xl font-semibold tracking-tight text-foreground transition-colors group-hover:text-primary">
{title}
</h3>
<p className="mb-4 line-clamp-2 text-sm text-muted-foreground">
{description}
</p>
<div className="flex flex-wrap gap-2">
{tags?.map((tag, index) => (
/* Badge replacement */
<span
key={index}
className="inline-flex items-center rounded-md bg-secondary/50 px-2 py-0.5 text-xs font-normal text-secondary-foreground hover:bg-secondary"
>
{tag}
</span>
))}
</div>
</div>
</div>
</motion.div>
);
}

View file

@ -1,6 +1,5 @@
"use client";
import { useId, useMemo, useState } from "react";
import {
AnimatePresence,
motion,
@ -8,6 +7,7 @@ import {
type Variants,
} from "framer-motion";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { useId, useMemo, useState } from "react";
interface Slide {
id: number;

View file

@ -1,6 +1,5 @@
"use client";
import { motion, useReducedMotion } from "framer-motion";
import {
Card,
CardContent,
@ -8,6 +7,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { motion, useReducedMotion } from "framer-motion";
const cards = [
{ title: "Card 1", description: "First card" },

View file

@ -3,13 +3,12 @@
import {
motion,
useMotionValue,
useReducedMotion,
useSpring,
useTransform,
useReducedMotion,
} from "framer-motion";
import { useState, useMemo } from "react";
import { CreditCard as CreditCardIcon } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { useMemo, useState } from "react";
interface CreditCardProps {
cardNumber?: string;
cardholderName?: string;

View file

@ -1,7 +1,7 @@
"use client";
import { useMemo, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
@ -10,16 +10,15 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { AnimatePresence, motion } from "framer-motion";
import {
Bold,
ChevronDown,
@ -32,6 +31,7 @@ import {
Underline,
X,
} from "lucide-react";
import { useMemo, useState } from "react";
type Priority = "high" | "medium" | "low";

View file

@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
@ -10,10 +10,10 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { Check, ShieldCheck, Sparkles, Star, Truck } from "lucide-react";
import { useState } from "react";
type Plan = {
id: string;

View file

@ -1,11 +1,11 @@
"use client";
import { motion } from "framer-motion";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Clock, ArrowRight, BookOpen } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import { BookOpen, Clock } from "lucide-react";
interface GlassBlogCardProps {
title?: string;

View file

@ -1,12 +1,12 @@
"use client";
import { motion } from "framer-motion";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { CreditCard, Lock, Calendar } from "lucide-react";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import { Calendar, CreditCard, Lock } from "lucide-react";
import { useState } from "react";
interface GlassCheckoutCardProps {

View file

@ -1,11 +1,11 @@
"use client";
import { motion } from "framer-motion";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import { ShoppingBag, CreditCard, ArrowRight } from "lucide-react";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import { ArrowRight, CreditCard, ShoppingBag } from "lucide-react";
interface OrderItem {
id: string;

View file

@ -1,16 +1,16 @@
"use client";
import { motion } from "framer-motion";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import {
ArrowUpRight,
ArrowDownLeft,
Wallet,
ArrowUpRight,
CreditCard,
TrendingUp,
Wallet,
} from "lucide-react";
import { cn } from "@/lib/utils";
interface GlassWalletCardProps {
balance?: string;

View file

@ -1,7 +1,7 @@
"use client";
import { motion, useReducedMotion } from "framer-motion";
import { Badge } from "@/components/ui/badge";
import { motion, useReducedMotion } from "framer-motion";
export function HoverExpandCard() {
const shouldReduceMotion = useReducedMotion();

View file

@ -1,10 +1,10 @@
"use client";
import { motion } from "framer-motion";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { ExternalLink, Github } from "lucide-react";
import { Card } from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import { ExternalLink, Github } from "lucide-react";
interface ProjectCardProps {
title?: string;

View file

@ -1,9 +1,8 @@
"use client";
import { useEffect, useRef, useState } from "react";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import {
ArrowUpRight,
ChevronDown,
@ -16,6 +15,7 @@ import {
Star,
Zap,
} from "lucide-react";
import { useEffect, useRef, useState } from "react";
type DropdownType = "share" | "quick" | "history" | "magic" | "model" | null;

View file

@ -1,12 +1,12 @@
"use client";
import { useState, useRef, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Send, User, Bot, Loader2 } from "lucide-react";
import { AnimatePresence, motion } from "framer-motion";
import { Bot, Loader2, Send, User } from "lucide-react";
import { useEffect, useRef, useState } from "react";
type Message = {
id: number;

View file

@ -1,7 +1,5 @@
"use client";
import { useCallback, useId, useState } from "react";
import { AnimatePresence, motion, type Variants } from "framer-motion";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
@ -11,16 +9,18 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
import { AnimatePresence, motion, type Variants } from "framer-motion";
import {
MessageSquare,
X,
Send,
Sparkles,
Zap,
Brain,
Code,
MessageSquare,
Send,
Sparkles,
X,
Zap,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { useCallback, useId, useState } from "react";
interface Agent {
id: string;

View file

@ -1,13 +1,12 @@
"use client";
import { FormEvent, useEffect, useMemo, useRef, useState } from "react";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import {
CheckCheck,
MoreVertical,
@ -17,6 +16,7 @@ import {
Send,
Video,
} from "lucide-react";
import { FormEvent, useEffect, useMemo, useRef, useState } from "react";
type Message = {
id: string;

View file

@ -1,29 +1,28 @@
"use client";
import { useState, useRef, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import {
MessageCircle,
Heart,
Share2,
MoreHorizontal,
CornerDownRight,
Send,
Smile,
Image as ImageIcon,
Paperclip,
X,
} from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Textarea } from "@/components/ui/textarea";
import { cn } from "@/lib/utils";
import { AnimatePresence, motion } from "framer-motion";
import {
CornerDownRight,
Heart,
Image as ImageIcon,
MessageCircle,
MoreHorizontal,
Paperclip,
Send,
Share2,
Smile,
} from "lucide-react";
import { useEffect, useRef, useState } from "react";
// ============================================================================
// TYPES

View file

@ -1,29 +1,29 @@
"use client";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { motion, type Variants } from "framer-motion";
import {
AlertTriangle,
Award,
BookOpen,
CheckCircle2,
ChevronRight,
Clock,
Download,
FileText,
Languages,
Lock,
Play,
PlayCircle,
Share2,
Star,
Timer,
Users,
Video,
CheckCircle2,
Lock,
FileText,
PlayCircle,
Timer,
AlertTriangle,
Zap,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { useState, useEffect } from "react";
import { useEffect, useState } from "react";
// ============================================================================
// TYPES & INTERFACES

View file

@ -1,6 +1,6 @@
"use client";
import { motion, AnimatePresence } from "framer-motion";
import { AnimatePresence, motion } from "framer-motion";
import { ChevronDown } from "lucide-react";
import { useState } from "react";

View file

@ -1,12 +1,12 @@
"use client";
import { useState } from "react";
import { motion, useReducedMotion } from "framer-motion";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { motion, useReducedMotion } from "framer-motion";
import { Check } from "lucide-react";
import { useState } from "react";
const planFeatures = [
"Unlimited projects",

View file

@ -1,10 +1,10 @@
"use client";
import { FormEvent, useState } from "react";
import { motion, useReducedMotion } from "framer-motion";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { motion, useReducedMotion } from "framer-motion";
import { FormEvent, useState } from "react";
export function GlassForgotPasswordCard() {
const shouldReduceMotion = useReducedMotion();

View file

@ -1,15 +1,15 @@
"use client";
import { FormEvent, useState } from "react";
import { motion, useReducedMotion } from "framer-motion";
import { Avatar } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Switch } from "@/components/ui/switch";
import { Avatar } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Textarea } from "@/components/ui/textarea";
import { motion, useReducedMotion } from "framer-motion";
import { UploadCloud } from "lucide-react";
import { FormEvent, useState } from "react";
export function GlassProfileSettingsCard() {
const shouldReduceMotion = useReducedMotion();

View file

@ -1,12 +1,12 @@
"use client";
import { FormEvent } from "react";
import { motion, useReducedMotion } from "framer-motion";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import { Github, Twitter, Chrome } from "lucide-react";
import { motion, useReducedMotion } from "framer-motion";
import { Chrome, Github, Twitter } from "lucide-react";
import { FormEvent } from "react";
const socialProviders = [
{ name: "Google", icon: Chrome },

View file

@ -1,12 +1,12 @@
"use client";
import { FormEvent, useState } from "react";
import { motion, useReducedMotion } from "framer-motion";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import { Github, Twitter, Chrome } from "lucide-react";
import { motion, useReducedMotion } from "framer-motion";
import { Chrome, Github, Twitter } from "lucide-react";
import { FormEvent, useState } from "react";
const socialProviders = [
{ name: "Google", icon: Chrome },

View file

@ -1,9 +1,9 @@
"use client";
import { useMemo, useState, FormEvent } from "react";
import { motion, useReducedMotion } from "framer-motion";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { motion, useReducedMotion } from "framer-motion";
import { FormEvent, useMemo, useState } from "react";
const CODE_LENGTH = 6;

View file

@ -1,8 +1,8 @@
"use client";
import { motion, AnimatePresence } from "framer-motion";
import { useState } from "react";
import { Input } from "@/components/ui/input";
import { motion } from "framer-motion";
import { useState } from "react";
export function FloatingLabelInput() {
const [isFocused, setIsFocused] = useState(false);

View file

@ -1,57 +1,43 @@
"use client";
import React, { useState, useMemo } from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";
import {
DndContext,
DragOverlay,
closestCorners,
defaultDropAnimationSideEffects,
DndContext,
DragEndEvent,
DragOverEvent,
DragOverlay,
DragStartEvent,
DropAnimation,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
DragStartEvent,
DragOverEvent,
DragEndEvent,
defaultDropAnimationSideEffects,
DropAnimation,
} from "@dnd-kit/core";
import {
arrayMove,
horizontalListSortingStrategy,
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
useSortable,
horizontalListSortingStrategy,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { motion, AnimatePresence } from "framer-motion";
import {
Plus,
MoreHorizontal,
Calendar,
MessageSquare,
Paperclip,
GripVertical,
Trash2,
Search,
Filter,
LayoutGrid,
List,
Clock,
CheckCircle2,
AlertCircle,
Filter,
MessageSquare,
MoreHorizontal,
Paperclip,
Plus,
Search,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { useMemo, useState } from "react";
// --- Types ---

View file

@ -1,7 +1,7 @@
"use client";
import { motion, type Variants } from "framer-motion";
import { Card } from "@/components/ui/card";
import { motion, type Variants } from "framer-motion";
import { CheckCircle2 } from "lucide-react";
const checklistItems = [

View file

@ -1,8 +1,8 @@
"use client";
import { useState } from "react";
import { Reorder } from "framer-motion";
import { GripVertical } from "lucide-react";
import { useState } from "react";
const initialItems = [
{ id: 1, text: "First item" },

View file

@ -1,6 +1,6 @@
"use client";
import { motion, AnimatePresence } from "framer-motion";
import { AnimatePresence, motion } from "framer-motion";
import { X } from "lucide-react";
import { useState } from "react";

View file

@ -1,23 +1,23 @@
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import {
Newspaper,
Clock,
ArrowRight,
TrendingUp,
MessageSquare,
Share2,
Bookmark,
MoreHorizontal,
Search,
Filter,
} from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { AnimatePresence, motion } from "framer-motion";
import {
ArrowRight,
Bookmark,
Clock,
Filter,
MessageSquare,
MoreHorizontal,
Newspaper,
Search,
Share2,
TrendingUp,
} from "lucide-react";
import { useState } from "react";
// ============================================================================
// TYPES

View file

@ -1,6 +1,8 @@
"use client";
import { useCallback, useState } from "react";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import {
AlertCircle,
@ -11,9 +13,7 @@ import {
LucideIcon,
X,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { useCallback, useState } from "react";
type NotificationType = "success" | "error" | "warning" | "info";

View file

@ -1,17 +1,17 @@
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
import {
MapPin,
Link as LinkIcon,
Calendar,
MoreHorizontal,
UserPlus,
} from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { motion } from "framer-motion";
import {
Calendar,
Link as LinkIcon,
MapPin,
MoreHorizontal,
UserPlus,
} from "lucide-react";
import { useState } from "react";
export function ProfilePage() {
const [isFollowing, setIsFollowing] = useState(false);

View file

@ -0,0 +1,284 @@
"use client";
import { NativeButton as Button } from "@/components/native/baseui/native-button-baseui";
import { Avatar } from "@base-ui/react/avatar";
import { motion, Variants } from "framer-motion";
import {
ArrowUpRight,
Github,
Globe,
Mail,
MapPin,
Twitter,
} from "lucide-react";
import * as React from "react";
const RESUME_DATA = {
profile: {
name: "Jordan Lee",
role: "Product Engineer",
avatarGradient:
"from-gray-100 to-gray-200 dark:from-zinc-800 dark:to-zinc-700",
socials: [
{ name: "GitHub", icon: Github, url: "#" },
{ name: "Twitter", icon: Twitter, url: "#" },
{ name: "Email", icon: Mail, url: "#" },
],
},
contact: {
website: "jordan.dev",
location: "San Francisco, CA",
},
stack: [
"Next.js",
"React",
"TypeScript",
"Node.js",
"PostgreSQL",
"Tailwind",
],
education: {
degree: "BS Computer Science",
school: "University of Technology",
period: "2016 - 2020",
},
about: {
text: "Product engineer focused on building accessible, pixel-perfect user interfaces. Currently designing systems at",
company: "Vercel",
suffix: ". Passionate about web performance and developer experience.",
},
experience: [
{
role: "Senior Frontend Engineer",
company: "Vercel",
period: "2022 - Present",
description: [
"Led the migration to Next.js App Router, improving load times by 35%.",
"Architected the internal component library used by 50+ engineers.",
],
},
{
role: "Software Engineer",
company: "Stripe",
period: "2020 - 2022",
description: [
"Built the new checkout experience, increasing conversion by 12%.",
"Implemented automated accessibility testing pipelines.",
],
},
],
projects: [
{
title: "Geist UI",
desc: "React component library inspired by Vercel's design system.",
stars: "4.2k",
},
{
title: "Next.js Conf",
desc: "Interactive conference platform built with Next.js and WebGL.",
stars: "2.1k",
},
],
};
function Separator({ className }: { className?: string }) {
return (
<div className={`w-full h-[1px] ${className}`} role="separator" />
);
}
export function MinimalResumeBaseUI() {
const container: Variants = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.05,
},
},
};
const item: Variants = {
hidden: { opacity: 0, y: 10 },
show: { opacity: 1, y: 0, transition: { duration: 0.4, ease: "easeOut" } },
};
return (
<div>
<motion.div
variants={container}
initial="hidden"
animate="show"
className="bg-white dark:bg-zinc-950 border border-gray-200 dark:border-zinc-800 rounded-lg overflow-hidden transition-colors duration-300"
>
<div className="grid grid-cols-1 md:grid-cols-12 min-h-[800px]">
{/* Sidebar */}
<div className="md:col-span-4 bg-gray-50/50 dark:bg-zinc-900/50 border-r border-gray-200 dark:border-zinc-800 p-8 flex flex-col gap-8 transition-colors duration-300">
{/* Profile Header */}
<motion.div variants={item} className="space-y-4">
<Avatar.Root
className={`flex h-16 w-16 shrink-0 overflow-hidden rounded-full border border-gray-200 dark:border-zinc-700 bg-gradient-to-tr ${RESUME_DATA.profile.avatarGradient}`}
>
{/* Using Fallback/div as there is no image but we want to use the primitive */}
<Avatar.Fallback className="flex h-full w-full items-center justify-center rounded-full bg-transparent" />
</Avatar.Root>
<div>
<h1 className="text-xl font-semibold tracking-tight text-gray-900 dark:text-zinc-100">
{RESUME_DATA.profile.name}
</h1>
<p className="text-sm text-gray-500 dark:text-zinc-400 font-mono mt-1">
{RESUME_DATA.profile.role}
</p>
</div>
<div className="flex gap-2">
{RESUME_DATA.profile.socials.map((social, index) => (
<Button
key={index}
variant="outline"
size="icon"
className="h-8 w-8 rounded-md border-gray-200 dark:border-zinc-800 hover:border-gray-300 dark:hover:border-zinc-700 hover:bg-white dark:hover:bg-zinc-800 text-gray-500 dark:text-zinc-400 hover:text-black dark:hover:text-zinc-100 transition-all p-0"
>
<social.icon className="h-4 w-4" />
</Button>
))}
</div>
</motion.div>
<Separator className="bg-gray-200 dark:bg-zinc-800" />
{/* Contact */}
<motion.div variants={item} className="space-y-3">
<h2 className="text-xs font-mono font-medium uppercase tracking-wider text-gray-500 dark:text-zinc-500">
Contact
</h2>
<div className="space-y-2 text-sm">
<a
href="#"
className="flex items-center gap-2 text-gray-600 dark:text-zinc-400 hover:text-black dark:hover:text-zinc-200 transition-colors group"
>
<Globe className="h-3.5 w-3.5 text-gray-400 dark:text-zinc-500 group-hover:text-gray-600 dark:group-hover:text-zinc-300" />
<span>{RESUME_DATA.contact.website}</span>
</a>
<div className="flex items-center gap-2 text-gray-600 dark:text-zinc-400">
<MapPin className="h-3.5 w-3.5 text-gray-400 dark:text-zinc-500" />
<span>{RESUME_DATA.contact.location}</span>
</div>
</div>
</motion.div>
{/* Skills */}
<motion.div variants={item} className="space-y-3">
<h2 className="text-xs font-mono font-medium uppercase tracking-wider text-gray-500 dark:text-zinc-500">
Stack
</h2>
<div className="flex flex-wrap gap-1.5">
{RESUME_DATA.stack.map((skill) => (
<span
key={skill}
className="px-2 py-1 bg-white dark:bg-zinc-900 border border-gray-200 dark:border-zinc-800 rounded text-[11px] font-medium text-gray-600 dark:text-zinc-400"
>
{skill}
</span>
))}
</div>
</motion.div>
{/* Education */}
<motion.div variants={item} className="space-y-3">
<h2 className="text-xs font-mono font-medium uppercase tracking-wider text-gray-500 dark:text-zinc-500">
Education
</h2>
<div>
<h3 className="font-medium text-gray-900 dark:text-zinc-100 text-sm">
{RESUME_DATA.education.degree}
</h3>
<p className="text-xs text-gray-500 dark:text-zinc-400 mt-0.5">
{RESUME_DATA.education.school}
</p>
<p className="text-[10px] text-gray-400 dark:text-zinc-500 mt-1 font-mono">
{RESUME_DATA.education.period}
</p>
</div>
</motion.div>
</div>
{/* Main Content */}
<div className="md:col-span-8 p-8 md:p-10 space-y-10 bg-white dark:bg-zinc-950 transition-colors duration-300">
{/* About */}
<motion.div variants={item} className="space-y-3">
<h2 className="text-xs font-mono font-medium uppercase tracking-wider text-gray-500 dark:text-zinc-500">
About
</h2>
<p className="text-sm text-gray-600 dark:text-zinc-400 leading-relaxed max-w-2xl">
{RESUME_DATA.about.text}{" "}
<span className="font-medium text-gray-900 dark:text-zinc-100">
{RESUME_DATA.about.company}
</span>
{RESUME_DATA.about.suffix}
</p>
</motion.div>
{/* Experience */}
<motion.div variants={item} className="space-y-6">
<h2 className="text-xs font-mono font-medium uppercase tracking-wider text-gray-500 dark:text-zinc-500">
Experience
</h2>
<div className="space-y-8">
{RESUME_DATA.experience.map((job, index) => (
<div key={index} className="group">
<div className="flex justify-between items-baseline mb-1">
<h3 className="font-medium text-gray-900 dark:text-zinc-100 text-sm">
{job.role}
</h3>
<span className="text-xs font-mono text-gray-400 dark:text-zinc-500">
{job.period}
</span>
</div>
<p className="text-xs text-gray-500 dark:text-zinc-400 mb-3">
{job.company}
</p>
<ul className="text-sm text-gray-600 dark:text-zinc-400 space-y-1.5 list-disc list-outside ml-3 marker:text-gray-300 dark:marker:text-zinc-700">
{job.description.map((desc, i) => (
<li key={i}>{desc}</li>
))}
</ul>
</div>
))}
</div>
</motion.div>
{/* Projects */}
<motion.div variants={item} className="space-y-4">
<h2 className="text-xs font-mono font-medium uppercase tracking-wider text-gray-500 dark:text-zinc-500">
Projects
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{RESUME_DATA.projects.map((project, i) => (
<div
key={i}
className="group p-3 rounded-md border border-gray-200 dark:border-zinc-800 hover:border-gray-300 dark:hover:border-zinc-700 hover:bg-gray-50/50 dark:hover:bg-zinc-900/50 transition-all cursor-pointer"
>
<div className="flex justify-between items-start mb-1.5">
<h3 className="font-medium text-sm text-gray-900 dark:text-zinc-100">
{project.title}
</h3>
<ArrowUpRight className="h-3.5 w-3.5 text-gray-400 dark:text-zinc-500 group-hover:text-gray-600 dark:group-hover:text-zinc-300 transition-colors" />
</div>
<p className="text-xs text-gray-500 dark:text-zinc-400 line-clamp-2 mb-2">
{project.desc}
</p>
<div className="flex items-center gap-1 text-[10px] text-gray-400 dark:text-zinc-500 font-mono">
<span> {project.stars}</span>
</div>
</div>
))}
</div>
</motion.div>
</div>
</div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,228 @@
"use client";
import { NativeButton as Button } from "@/components/native/baseui/native-button-baseui";
import { motion, Variants } from "framer-motion";
import { Download, Globe, Linkedin, Mail, MapPin, Phone } from "lucide-react";
import * as React from "react";
const RESUME_DATA = {
personalInfo: {
name: "Alex Morgan",
title: "Senior Software Engineer",
summary:
"Results-oriented Senior Software Engineer with over 8 years of experience in designing, developing, and deploying scalable web applications. Proven track record of leadership, mentoring junior developers, and driving technical innovation. Expert in full-stack development with a focus on React, Node.js, and cloud architecture.",
email: "alex.morgan@example.com",
phone: "(555) 123-4567",
location: "New York, NY",
linkedin: "linkedin.com/in/alexmorgan",
website: "alexmorgan.dev",
},
experience: [
{
company: "TechCorp Solutions",
role: "Senior Software Engineer",
period: "2020 - Present",
description: [
"Architected and led the development of a microservices-based e-commerce platform, handling over 100k daily active users.",
"Reduced server costs by 40% through optimization of AWS infrastructure and implementation of serverless functions.",
"Mentored a team of 5 junior developers, conducting code reviews and facilitating technical workshops.",
],
},
{
company: "Innovate Inc.",
role: "Software Engineer",
period: "2017 - 2020",
description: [
"Developed key features for the company's flagship SaaS product using React and Redux.",
"Implemented a real-time notification system using WebSockets, improving user engagement by 25%.",
"Collaborated with product managers and designers to define requirements and deliver high-quality user experiences.",
],
},
],
skills: {
languages:
"JavaScript (ES6+), TypeScript, Python, React, Next.js, Node.js, Express, Django",
tools:
"AWS (EC2, Lambda, S3), Docker, Kubernetes, Git, CI/CD (GitHub Actions), PostgreSQL, MongoDB",
},
education: {
school: "University of Technology",
degree: "Bachelor of Science in Computer Science",
period: "2013 - 2017",
},
certifications: [
"AWS Certified Solutions Architect Associate",
"Meta Front-End Developer Professional Certificate",
],
};
function Separator({ className }: { className?: string }) {
return (
<div className={`w-full h-[1px] bg-border ${className}`} role="separator" />
);
}
export function ProfessionalResumeBaseUI() {
const fadeIn: Variants = {
hidden: { opacity: 0, y: 10 },
show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: "easeOut" } },
};
const staggerContainer: Variants = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
return (
<div className="">
<motion.div
variants={staggerContainer}
initial="hidden"
animate="show"
className="p-8 md:p-12 space-y-8"
>
{/* Header */}
<motion.div variants={fadeIn} className="text-center space-y-4">
<h1 className="text-4xl md:text-5xl font-bold text-foreground tracking-tight">
{RESUME_DATA.personalInfo.name}
</h1>
<p className="text-lg text-muted-foreground font-sans uppercase tracking-widest text-sm">
{RESUME_DATA.personalInfo.title}
</p>
<div className="flex flex-wrap justify-center gap-4 text-sm text-muted-foreground font-sans pt-2">
<div className="flex items-center gap-1.5">
<Mail className="h-4 w-4" />
<span>{RESUME_DATA.personalInfo.email}</span>
</div>
<div className="flex items-center gap-1.5">
<Phone className="h-4 w-4" />
<span>{RESUME_DATA.personalInfo.phone}</span>
</div>
<div className="flex items-center gap-1.5">
<MapPin className="h-4 w-4" />
<span>{RESUME_DATA.personalInfo.location}</span>
</div>
<div className="flex items-center gap-1.5">
<Linkedin className="h-4 w-4" />
<span>{RESUME_DATA.personalInfo.linkedin}</span>
</div>
<div className="flex items-center gap-1.5">
<Globe className="h-4 w-4" />
<span>{RESUME_DATA.personalInfo.website}</span>
</div>
</div>
</motion.div>
<Separator />
{/* Summary */}
<motion.div variants={fadeIn} className="space-y-3">
<h2 className="text-xl font-bold text-foreground border-b-2 border-foreground pb-1 inline-block">
Professional Summary
</h2>
<p className="text-card-foreground leading-relaxed font-sans text-sm md:text-base">
{RESUME_DATA.personalInfo.summary}
</p>
</motion.div>
{/* Experience */}
<motion.div variants={fadeIn} className="space-y-6">
<h2 className="text-xl font-bold text-foreground border-b-2 border-foreground pb-1 inline-block">
Experience
</h2>
<div className="space-y-6">
{RESUME_DATA.experience.map((job, index) => (
<div key={index}>
<div className="flex justify-between items-baseline mb-1">
<h3 className="text-lg font-bold text-foreground">
{job.company}
</h3>
<span className="text-sm font-sans text-muted-foreground">
{job.period}
</span>
</div>
<p className="text-card-foreground font-medium italic mb-2">
{job.role}
</p>
<ul className="list-disc list-outside ml-5 space-y-1 text-card-foreground font-sans text-sm">
{job.description.map((item, i) => (
<li key={i}>{item}</li>
))}
</ul>
</div>
))}
</div>
</motion.div>
{/* Skills */}
<motion.div variants={fadeIn} className="space-y-3">
<h2 className="text-xl font-bold text-foreground border-b-2 border-foreground pb-1 inline-block">
Technical Skills
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 font-sans text-sm">
<div>
<span className="font-bold text-foreground block mb-1">
Languages & Frameworks:
</span>
<p className="text-card-foreground">
{RESUME_DATA.skills.languages}
</p>
</div>
<div>
<span className="font-bold text-foreground block mb-1">
Tools & Platforms:
</span>
<p className="text-card-foreground">{RESUME_DATA.skills.tools}</p>
</div>
</div>
</motion.div>
{/* Education */}
<motion.div variants={fadeIn} className="space-y-3">
<h2 className="text-xl font-bold text-foreground border-b-2 border-foreground pb-1 inline-block">
Education
</h2>
<div>
<div className="flex justify-between items-baseline">
<h3 className="text-lg font-bold text-foreground">
{RESUME_DATA.education.school}
</h3>
<span className="text-sm font-sans text-muted-foreground">
{RESUME_DATA.education.period}
</span>
</div>
<p className="text-card-foreground font-sans">
{RESUME_DATA.education.degree}
</p>
</div>
</motion.div>
{/* Certifications */}
<motion.div variants={fadeIn} className="space-y-3">
<h2 className="text-xl font-bold text-foreground border-b-2 border-foreground pb-1 inline-block">
Certifications
</h2>
<ul className="list-disc list-outside ml-5 space-y-1 text-card-foreground font-sans text-sm">
{RESUME_DATA.certifications.map((cert, index) => (
<li key={index}>{cert}</li>
))}
</ul>
</motion.div>
<div className="pt-8 flex justify-center">
<Button className="gap-2">
<Download className="h-4 w-4" />
Download PDF
</Button>
</div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,437 @@
"use client";
import { NativeButton as Button } from "@/components/native/baseui/native-button-baseui";
import { Avatar } from "@base-ui/react/avatar";
import { motion } from "framer-motion";
import {
Download,
Github,
Globe,
Linkedin,
Mail,
MapPin,
Phone,
Star,
} from "lucide-react";
import * as React from "react";
const AVATAR_IMAGE_URL =
"https://iimydr2b8o.ufs.sh/f/Zqn6AViLMoTtoUjLg4dAryGEidskK72wBCQA6DNcZH4Xh5b8";
const RESUME_DATA = {
personalInfo: {
name: "John Doe",
title: "Senior Full Stack Developer",
summary:
"Passionate developer with 8+ years of experience building scalable web applications. Specializing in React, Node.js, and cloud architecture.",
location: "San Francisco, CA",
email: "john.doe@example.com",
phone: "+1 (555) 123-4567",
avatarUrl: AVATAR_IMAGE_URL,
initials: "JD",
socials: [
{ name: "GitHub", icon: Github, url: "#" },
{ name: "LinkedIn", icon: Linkedin, url: "#" },
{ name: "Portfolio", icon: Globe, url: "#" },
],
},
experience: [
{
role: "Senior Full Stack Developer",
company: "Tech Corp Inc.",
period: "2021 - Present",
location: "San Francisco, CA",
achievements: [
"Led the frontend team in rebuilding the core product using Next.js 14 and React, improving performance by 40%",
"Architected and implemented a microservices-based backend using Node.js and GraphQL",
"Mentored 5 junior developers and conducted weekly code review sessions",
],
},
{
role: "Full Stack Developer",
company: "StartUp Ltd.",
period: "2019 - 2021",
location: "Remote",
achievements: [
"Developed and maintained multiple client-facing applications with 99.9% uptime",
"Implemented CI/CD pipelines using GitHub Actions, reducing deployment time by 60%",
"Built a real-time analytics dashboard using React and WebSockets",
],
},
{
role: "Junior Developer",
company: "Digital Agency",
period: "2017 - 2019",
location: "New York, NY",
achievements: [
"Contributed to 20+ client projects using React, Vue.js, and WordPress",
"Optimized web applications for SEO and accessibility (WCAG 2.1 AA)",
],
},
],
projects: [
{
name: "E-commerce Platform",
tech: ["Next.js", "Stripe", "Prisma"],
desc: "A full-featured online store with real-time inventory management and payment processing.",
stars: "2.3k",
},
{
name: "Task Management App",
tech: ["React", "Firebase", "Tailwind"],
desc: "Collaborative task manager with drag-and-drop interface and real-time updates.",
stars: "1.8k",
},
{
name: "Analytics Dashboard",
tech: ["Vue.js", "D3.js", "Node.js"],
desc: "Real-time data visualization platform with custom chart components.",
stars: "1.2k",
},
{
name: "Social Media API",
tech: ["GraphQL", "PostgreSQL", "Redis"],
desc: "Scalable REST and GraphQL API serving 100k+ daily requests.",
stars: "890",
},
],
skills: [
{
category: "Frontend",
skills: ["React", "Next.js", "TypeScript", "Tailwind CSS", "Vue.js"],
},
{
category: "Backend",
skills: ["Node.js", "Express", "GraphQL", "PostgreSQL", "MongoDB"],
},
{
category: "DevOps & Tools",
skills: ["Docker", "AWS", "CI/CD", "Git", "Vercel"],
},
],
education: [
{
degree: "BS Computer Science",
institution: "University of Technology",
period: "2015 - 2019",
extra: "GPA: 3.8/4.0",
},
{
degree: "Full Stack Web Development",
institution: "Code Academy Bootcamp",
period: "2019",
extra: "Certificate",
},
{
degree: "AWS Certified Solutions Architect",
institution: "Amazon Web Services",
period: "2022",
extra: null,
},
],
languages: [
{ lang: "English", level: "Native", percentage: 100 },
{ lang: "Spanish", level: "Professional", percentage: 75 },
{ lang: "French", level: "Intermediate", percentage: 50 },
{ lang: "Mandarin", level: "Basic", percentage: 30 },
],
};
function Separator({ className }: { className?: string }) {
return (
<div className={`w-full h-[1px] bg-border ${className}`} role="separator" />
);
}
function Badge({
children,
variant = "default",
className = "",
}: {
children: React.ReactNode;
variant?: "default" | "secondary" | "outline";
className?: string;
}) {
const baseStyles =
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors";
const variantStyles = {
default: "bg-primary text-primary-foreground",
secondary: "bg-secondary text-secondary-foreground",
outline: "border border-input bg-background text-foreground",
};
return (
<span className={`${baseStyles} ${variantStyles[variant]} ${className}`}>
{children}
</span>
);
}
export function ResumeCardBaseUI() {
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.08,
},
},
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 },
};
return (
<div className="w-full max-w-4xl mx-auto p-6 md:p-8 bg-background">
<motion.div
variants={container}
initial="hidden"
animate="show"
className="space-y-12"
>
{/* Header Section */}
<motion.div variants={item} className="space-y-6">
<div className="flex flex-col md:flex-row gap-6 items-center md:items-start">
<motion.div
whileHover={{ scale: 1.05 }}
transition={{ duration: 0.2 }}
>
<Avatar.Root className="w-24 h-24 md:w-28 md:h-28 relative flex shrink-0 overflow-hidden rounded-full">
<Avatar.Image
src={RESUME_DATA.personalInfo.avatarUrl}
className="aspect-square h-full w-full object-cover"
/>
<Avatar.Fallback className="flex h-full w-full items-center justify-center rounded-full bg-muted text-lg font-semibold">
{RESUME_DATA.personalInfo.initials}
</Avatar.Fallback>
</Avatar.Root>
</motion.div>
<div className="flex-1 text-center md:text-left space-y-3">
<div>
<h1 className="text-3xl md:text-4xl font-bold text-foreground tracking-tight">
{RESUME_DATA.personalInfo.name}
</h1>
<p className="text-lg text-muted-foreground font-medium mt-1">
{RESUME_DATA.personalInfo.title}
</p>
</div>
<p className="text-sm text-muted-foreground max-w-2xl leading-relaxed">
{RESUME_DATA.personalInfo.summary}
</p>
<div className="flex flex-wrap justify-center md:justify-start gap-x-4 gap-y-2 text-sm text-muted-foreground pt-2">
<div className="flex items-center gap-1.5">
<MapPin className="w-4 h-4" />
<span>{RESUME_DATA.personalInfo.location}</span>
</div>
<div className="flex items-center gap-1.5">
<Mail className="w-4 h-4" />
<span>{RESUME_DATA.personalInfo.email}</span>
</div>
<div className="flex items-center gap-1.5">
<Phone className="w-4 h-4" />
<span>{RESUME_DATA.personalInfo.phone}</span>
</div>
</div>
<div className="flex flex-wrap justify-center md:justify-start gap-2 pt-2">
{RESUME_DATA.personalInfo.socials.map((social, index) => (
<Button
key={index}
variant="outline"
size="sm"
className="gap-2 h-9"
>
<social.icon className="w-4 h-4" />
{social.name}
</Button>
))}
<Button size="sm" className="gap-2 h-9">
<Download className="w-4 h-4" />
Download PDF
</Button>
</div>
</div>
</div>
</motion.div>
<Separator />
{/* Experience */}
<motion.div variants={item} className="space-y-6">
<h2 className="text-2xl font-bold flex items-center gap-2">
Experience
</h2>
<div className="space-y-8">
{RESUME_DATA.experience.map((job, index) => (
<div key={index} className="space-y-3">
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-2">
<div>
<h3 className="text-lg font-semibold text-foreground">
{job.role}
</h3>
<p className="font-medium">{job.company}</p>
<p className="text-xs text-muted-foreground">
{job.location}
</p>
</div>
<Badge variant="secondary" className="w-fit">
{job.period}
</Badge>
</div>
<ul className="space-y-1.5 text-sm text-muted-foreground">
{job.achievements.map((achievement, i) => (
<li key={i} className="flex gap-2">
<span className="text-accent mt-0.5"></span>
<span>{achievement}</span>
</li>
))}
</ul>
{index < RESUME_DATA.experience.length - 1 && (
<Separator className="mt-6" />
)}
</div>
))}
</div>
</motion.div>
<Separator />
{/* Projects */}
<motion.div variants={item} className="space-y-6">
<h2 className="text-2xl font-bold flex items-center gap-2">
Featured Projects
</h2>
<div className="grid sm:grid-cols-2 gap-6">
{RESUME_DATA.projects.map((project, index) => (
<motion.div
key={index}
whileHover={{ y: -4 }}
transition={{ duration: 0.2 }}
className="p-5 rounded-lg bg-accent/5 hover:bg-accent/10 transition-colors cursor-pointer space-y-3"
>
<div className="flex justify-between items-start">
<h3 className="font-semibold text-foreground">
{project.name}
</h3>
<div className="flex items-center gap-1 text-xs text-muted-foreground">
<Star className="w-3 h-3 fill-accent text-accent" />
<span>{project.stars}</span>
</div>
</div>
<p className="text-sm text-muted-foreground">{project.desc}</p>
<div className="flex flex-wrap gap-1.5">
{project.tech.map((tech) => (
<Badge key={tech} variant="outline" className="text-xs">
{tech}
</Badge>
))}
</div>
</motion.div>
))}
</div>
</motion.div>
<Separator />
{/* Skills */}
<motion.div variants={item} className="space-y-6">
<h2 className="text-2xl font-bold flex items-center gap-2">Skills</h2>
<div className="space-y-5">
{RESUME_DATA.skills.map((group, index) => (
<div key={index}>
<h4 className="text-sm font-semibold text-muted-foreground mb-3">
{group.category}
</h4>
<div className="flex flex-wrap gap-2">
{group.skills.map((skill) => (
<Badge
key={skill}
variant="secondary"
className="hover:bg-accent hover:text-accent-foreground transition-colors cursor-default"
>
{skill}
</Badge>
))}
</div>
</div>
))}
</div>
</motion.div>
<Separator />
{/* Education */}
<motion.div variants={item} className="space-y-6">
<h2 className="text-2xl font-bold flex items-center gap-2">
Education
</h2>
<div className="space-y-5">
{RESUME_DATA.education.map((edu, index) => (
<div key={index}>
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-2">
<div>
<h3 className="font-semibold text-foreground">
{edu.degree}
</h3>
<p className="text-sm text-muted-foreground">
{edu.institution}
</p>
</div>
<span className="text-xs text-muted-foreground">
{edu.period}
</span>
</div>
{edu.extra && (
<Badge variant="outline" className="mt-2 text-xs">
{edu.extra}
</Badge>
)}
</div>
))}
</div>
</motion.div>
<Separator />
{/* Languages */}
<motion.div variants={item} className="space-y-6">
<h2 className="text-2xl font-bold flex items-center gap-2">
Languages
</h2>
<div className="grid sm:grid-cols-2 gap-6">
{RESUME_DATA.languages.map((language, index) => (
<div key={index} className="space-y-2">
<div className="flex justify-between items-center">
<span className="text-sm font-medium text-foreground">
{language.lang}
</span>
<span className="text-xs text-muted-foreground">
{language.level}
</span>
</div>
<div className="w-full bg-accent/20 h-2 rounded-full overflow-hidden">
<motion.div
initial={{ width: 0 }}
animate={{ width: `${language.percentage}%` }}
transition={{ duration: 1, delay: 0.5 + index * 0.1 }}
className="bg-accent h-full rounded-full"
/>
</div>
</div>
))}
</div>
</motion.div>
</motion.div>
</div>
);
}

View file

@ -0,0 +1,536 @@
"use client";
import { NativeButton as Button } from "@/components/native/baseui/native-button-baseui";
import { Avatar } from "@base-ui/react/avatar";
import { motion, Variants } from "framer-motion";
import {
Briefcase,
Building2,
Code2,
Download,
ExternalLink,
Globe,
GraduationCap,
Languages,
Linkedin,
Mail,
MapPin,
Phone,
} from "lucide-react";
import * as React from "react";
const AVATAR_IMAGE_URL =
"https://iimydr2b8o.ufs.sh/f/Zqn6AViLMoTtoUjLg4dAryGEidskK72wBCQA6DNcZH4Xh5b8";
const RESUME_DATA = {
personalInfo: {
name: "Jordan Davis",
title: "Senior Full Stack Engineer",
summary:
"Passionate developer with 5+ years of experience building scalable web applications. Dedicated to writing clean, efficient code and creating intuitive user experiences that delight users.",
email: "jordan.davis@example.com",
phone: "(555) 987-6543",
location: "San Francisco, CA",
avatarUrl: AVATAR_IMAGE_URL,
initials: "JD",
},
experience: [
{
role: "Senior Frontend Engineer",
company: "TechFlow Systems",
period: "2021 - Present",
description: [
"Spearheaded the migration of legacy codebase to Next.js, improving load times by 40%.",
"Implemented a comprehensive design system using Tailwind CSS and Storybook, reducing development time by 25%.",
"Mentored junior developers and established code quality standards through rigorous code reviews.",
],
},
{
role: "Software Developer",
company: "Creative Solutions Inc.",
period: "2018 - 2021",
description: [
"Developed and maintained client-facing web applications using React and Redux.",
"Collaborated with UX designers to implement responsive and accessible interfaces compliant with WCAG 2.1.",
"Integrated third-party APIs for payment processing and data visualization.",
],
},
],
projects: [
{
title: "E-commerce Dashboard",
type: "Open Source",
description:
"A comprehensive analytics dashboard for online retailers featuring real-time data visualization, inventory management, and sales forecasting.",
tech: ["React", "D3.js", "Node.js"],
},
{
title: "Task Management App",
type: "SaaS",
description:
"Collaborative project management tool with real-time updates, team chat functionality, and automated workflow triggers.",
tech: ["Vue.js", "Firebase", "Tailwind"],
},
],
education: [
{
degree: "Master of Computer Science",
school: "Stanford University",
year: "2016 - 2018",
},
{
degree: "BS in Software Engineering",
school: "MIT",
year: "2012 - 2016",
},
],
skills: {
frontend: ["React", "Next.js", "TypeScript", "Tailwind", "Framer Motion"],
backend: ["Node.js", "PostgreSQL", "GraphQL", "Redis"],
tools: ["Git", "Docker", "AWS", "Figma"],
},
languages: [
{ name: "English", level: "Native" },
{ name: "Spanish", level: "Fluent" },
{ name: "French", level: "Intermediate" },
],
};
function Separator({ className }: { className?: string }) {
return (
<div className={`w-full h-[1px] bg-border/60 ${className}`} role="separator" />
);
}
function Badge({
children,
variant = "default",
className = "",
}: {
children: React.ReactNode;
variant?: "default" | "secondary" | "outline";
className?: string;
}) {
const baseStyles =
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors";
const variantStyles = {
default: "bg-primary text-primary-foreground shadow-sm",
secondary: "bg-secondary text-secondary-foreground",
outline: "border border-input bg-background text-foreground",
};
return (
<span className={`${baseStyles} ${variantStyles[variant]} ${className}`}>
{children}
</span>
);
}
function Card({
children,
className = "",
}: {
children: React.ReactNode;
className?: string;
}) {
return (
<div className={`rounded-lg border bg-card text-card-foreground ${className}`}>
{children}
</div>
);
}
function CardContent({
children,
className = "",
}: {
children: React.ReactNode;
className?: string;
}) {
return <div className={`p-6 ${className}`}>{children}</div>;
}
export function StandardResumeBaseUI() {
const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2,
},
},
};
const itemVariants: Variants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: "easeOut" },
},
};
return (
<div className="">
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="bg-card text-card-foreground rounded-2xl shadow-xl border border-border/50 overflow-hidden"
>
{/* Header Section with Gradient */}
<motion.div
variants={itemVariants}
className="relative bg-gradient-to-br from-primary/5 via-muted/50 to-background p-8 md:p-12 border-b border-border/60"
>
<div className="relative z-10 flex flex-col md:flex-row gap-8 items-center md:items-start text-center md:text-left">
<motion.div
whileHover={{ scale: 1.05 }}
transition={{ type: "spring", stiffness: 300 }}
className="relative"
>
<div className="absolute inset-0 rounded-full bg-primary/20 blur-xl transform scale-110" />
<Avatar.Root className="h-32 w-32 md:h-40 md:w-40 border-4 border-background shadow-2xl relative flex shrink-0 overflow-hidden rounded-full">
<Avatar.Image
src={RESUME_DATA.personalInfo.avatarUrl}
className="aspect-square h-full w-full object-cover"
/>
<Avatar.Fallback className="flex h-full w-full items-center justify-center rounded-full bg-primary/10 text-primary text-4xl font-bold">
{RESUME_DATA.personalInfo.initials}
</Avatar.Fallback>
</Avatar.Root>
</motion.div>
<div className="flex-1 space-y-4">
<div>
<h1 className="text-4xl md:text-5xl font-extrabold tracking-tight text-foreground mb-2">
{RESUME_DATA.personalInfo.name}
</h1>
<p className="text-xl md:text-2xl text-primary font-medium tracking-wide">
{RESUME_DATA.personalInfo.title}
</p>
</div>
<p className="text-muted-foreground max-w-2xl mx-auto md:mx-0 leading-relaxed text-base md:text-lg">
{RESUME_DATA.personalInfo.summary}
</p>
<div className="flex flex-wrap justify-center md:justify-start gap-3 pt-2">
<Button
variant="outline"
size="sm"
className="gap-2 h-9 rounded-full bg-background/50 backdrop-blur-sm hover:bg-primary/10 hover:text-primary hover:border-primary/50 transition-all duration-300"
>
<Mail className="h-3.5 w-3.5" />
<span>{RESUME_DATA.personalInfo.email}</span>
</Button>
<Button
variant="outline"
size="sm"
className="gap-2 h-9 rounded-full bg-background/50 backdrop-blur-sm hover:bg-primary/10 hover:text-primary hover:border-primary/50 transition-all duration-300"
>
<Phone className="h-3.5 w-3.5" />
<span>{RESUME_DATA.personalInfo.phone}</span>
</Button>
<Button
variant="outline"
size="sm"
className="gap-2 h-9 rounded-full bg-background/50 backdrop-blur-sm hover:bg-primary/10 hover:text-primary hover:border-primary/50 transition-all duration-300"
>
<MapPin className="h-3.5 w-3.5" />
<span>{RESUME_DATA.personalInfo.location}</span>
</Button>
</div>
</div>
<div className="flex flex-col gap-3 min-w-[160px]">
<Button className="w-full gap-2 shadow-lg shadow-primary/20 hover:shadow-primary/40 transition-all duration-300 rounded-full">
<Download className="h-4 w-4" />
Download CV
</Button>
<div className="flex gap-2 justify-center">
<Button
variant="ghost"
size="icon"
className="h-10 w-10 text-muted-foreground hover:text-primary hover:bg-primary/10 rounded-full transition-all duration-300"
>
<Linkedin className="h-5 w-5" />
<span className="sr-only">LinkedIn</span>
</Button>
<Button
variant="ghost"
size="icon"
className="h-10 w-10 text-muted-foreground hover:text-primary hover:bg-primary/10 rounded-full transition-all duration-300"
>
<Globe className="h-5 w-5" />
<span className="sr-only">Website</span>
</Button>
</div>
</div>
</div>
</motion.div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-0 lg:divide-x divide-border/60">
{/* Main Content Column */}
<div className="lg:col-span-2 p-8 md:p-10 space-y-10">
{/* Experience Section */}
<motion.section
variants={itemVariants}
aria-labelledby="experience-heading"
>
<div className="flex items-center gap-3 mb-6">
<div className="p-2.5 bg-primary/10 rounded-xl text-primary shadow-sm">
<Briefcase className="h-5 w-5" />
</div>
<h2
id="experience-heading"
className="text-2xl font-bold text-foreground tracking-tight"
>
Work Experience
</h2>
</div>
<div className="space-y-8 relative pl-2">
{RESUME_DATA.experience.map((job, index) => (
<div key={index} className="relative pl-8 group">
<div className="absolute left-0 top-1.5 h-5 w-5 rounded-full border-4 border-background bg-primary shadow-md group-hover:scale-125 transition-transform duration-300" />
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-baseline mb-2">
<h3 className="font-bold text-lg text-foreground group-hover:text-primary transition-colors">
{job.role}
</h3>
<Badge variant="secondary" className="font-medium w-fit">
{job.period}
</Badge>
</div>
<div className="flex items-center gap-2 text-primary font-semibold text-sm mb-3">
<Building2 className="h-3.5 w-3.5" />
<span>{job.company}</span>
</div>
<ul className="list-disc list-outside ml-4 space-y-2 text-sm text-muted-foreground/90 leading-relaxed">
{job.description.map((item, i) => (
<li
key={i}
dangerouslySetInnerHTML={{
__html: item.replace(
/(Next\.js|Tailwind CSS|React)/g,
'<span class="font-medium text-foreground">$1</span>'
),
}}
/>
))}
</ul>
</div>
))}
</div>
</motion.section>
<Separator className="bg-border/60" />
{/* Projects Section */}
<motion.section
variants={itemVariants}
aria-labelledby="projects-heading"
>
<div className="flex items-center gap-3 mb-6">
<div className="p-2.5 bg-primary/10 rounded-xl text-primary shadow-sm">
<Code2 className="h-5 w-5" />
</div>
<h2
id="projects-heading"
className="text-2xl font-bold text-foreground tracking-tight"
>
Featured Projects
</h2>
</div>
<div className="grid gap-5">
{RESUME_DATA.projects.map((project, index) => (
<motion.div
key={index}
whileHover={{ y: -4 }}
transition={{ type: "spring", stiffness: 300 }}
>
<Card className="bg-gradient-to-br from-card to-muted/20 border-border/50 hover:border-primary/50 hover:shadow-lg transition-all duration-300">
<CardContent className="p-5">
<div className="flex justify-between items-start mb-3">
<div className="flex items-center gap-2">
<h3 className="font-bold text-lg">
{project.title}
</h3>
<ExternalLink className="h-3.5 w-3.5 text-muted-foreground" />
</div>
<Badge
variant={
project.type === "Open Source"
? "default"
: "outline"
}
className="text-[10px] uppercase tracking-wider font-bold shadow-sm"
>
{project.type}
</Badge>
</div>
<p className="text-sm text-muted-foreground mb-4 leading-relaxed">
{project.description}
</p>
<div className="flex flex-wrap gap-2">
{project.tech.map((tech, i) => (
<Badge
key={i}
variant="secondary"
className="text-[10px] bg-primary/5 text-primary border-primary/10"
>
{tech}
</Badge>
))}
</div>
</CardContent>
</Card>
</motion.div>
))}
</div>
</motion.section>
</div>
{/* Sidebar Column */}
<div className="bg-muted/10 p-8 md:p-10 space-y-10 h-full">
{/* Education */}
<motion.section
variants={itemVariants}
aria-labelledby="education-heading"
>
<div className="flex items-center gap-3 mb-5">
<GraduationCap className="h-5 w-5 text-primary" />
<h2
id="education-heading"
className="text-lg font-bold text-foreground tracking-tight uppercase"
>
Education
</h2>
</div>
<div className="space-y-6">
{RESUME_DATA.education.map((edu, index) => (
<div
key={index}
className="relative pl-4 border-l-2 border-primary/20"
>
<h3 className="font-bold text-base">{edu.degree}</h3>
<div className="text-sm text-primary font-medium mb-1">
{edu.school}
</div>
<div className="text-xs text-muted-foreground font-medium uppercase tracking-wider">
{edu.year}
</div>
</div>
))}
</div>
</motion.section>
<Separator className="bg-border/60" />
{/* Skills */}
<motion.section
variants={itemVariants}
aria-labelledby="skills-heading"
>
<div className="flex items-center gap-3 mb-5">
<Code2 className="h-5 w-5 text-primary" />
<h2
id="skills-heading"
className="text-lg font-bold text-foreground tracking-tight uppercase"
>
Skills
</h2>
</div>
<div className="space-y-6">
<div>
<h3 className="text-xs font-bold uppercase tracking-wider text-muted-foreground mb-3 flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-primary" />{" "}
Frontend
</h3>
<div className="flex flex-wrap gap-2">
{RESUME_DATA.skills.frontend.map((skill, i) => (
<Badge
key={i}
className="bg-background hover:bg-primary hover:text-primary-foreground text-foreground border-border transition-colors duration-300"
>
{skill}
</Badge>
))}
</div>
</div>
<div>
<h3 className="text-xs font-bold uppercase tracking-wider text-muted-foreground mb-3 flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-primary" />{" "}
Backend
</h3>
<div className="flex flex-wrap gap-2">
{RESUME_DATA.skills.backend.map((skill, i) => (
<Badge
key={i}
className="bg-background hover:bg-primary hover:text-primary-foreground text-foreground border-border transition-colors duration-300"
>
{skill}
</Badge>
))}
</div>
</div>
<div>
<h3 className="text-xs font-bold uppercase tracking-wider text-muted-foreground mb-3 flex items-center gap-2">
<span className="w-1.5 h-1.5 rounded-full bg-primary" />{" "}
Tools
</h3>
<div className="flex flex-wrap gap-2">
{RESUME_DATA.skills.tools.map((skill, i) => (
<Badge
key={i}
className="bg-background hover:bg-primary hover:text-primary-foreground text-foreground border-border transition-colors duration-300"
>
{skill}
</Badge>
))}
</div>
</div>
</div>
</motion.section>
<Separator className="bg-border/60" />
{/* Languages */}
<motion.section
variants={itemVariants}
aria-labelledby="languages-heading"
>
<div className="flex items-center gap-3 mb-5">
<Languages className="h-5 w-5 text-primary" />
<h2
id="languages-heading"
className="text-lg font-bold text-foreground tracking-tight uppercase"
>
Languages
</h2>
</div>
<div className="space-y-3">
{RESUME_DATA.languages.map((lang, index) => (
<div
key={index}
className="flex justify-between items-center p-2 rounded-lg bg-background/50 border border-border/50"
>
<span className="font-medium">{lang.name}</span>
<Badge variant="secondary" className="text-xs">
{lang.level}
</Badge>
</div>
))}
</div>
</motion.section>
</div>
</div>
</motion.div>
</div>
);
}

View file

@ -1,18 +1,16 @@
"use client";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { motion, Variants } from "framer-motion";
import {
ArrowUpRight,
Github,
Twitter,
Mail,
Link as LinkIcon,
MapPin,
Globe,
Mail,
MapPin,
Twitter,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
const RESUME_DATA = {
profile: {

View file

@ -1,9 +1,9 @@
"use client";
import { motion, Variants } from "framer-motion";
import { Mail, Phone, MapPin, Linkedin, Globe, Download } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { motion, Variants } from "framer-motion";
import { Download, Globe, Linkedin, Mail, MapPin, Phone } from "lucide-react";
const RESUME_DATA = {
personalInfo: {

View file

@ -1,20 +1,20 @@
"use client";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { motion } from "framer-motion";
import {
Download,
Mail,
Phone,
MapPin,
Github,
Linkedin,
Globe,
Linkedin,
Mail,
MapPin,
Phone,
Star,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Separator } from "@/components/ui/separator";
const RESUME_DATA = {
personalInfo: {

View file

@ -1,25 +1,25 @@
"use client";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import { motion, Variants } from "framer-motion";
import {
Mail,
Phone,
MapPin,
Linkedin,
Globe,
Briefcase,
Building2,
Code2,
Download,
ExternalLink,
Building2,
Briefcase,
Globe,
GraduationCap,
Code2,
Languages,
Linkedin,
Mail,
MapPin,
Phone,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "@/components/ui/card";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
const RESUME_DATA = {
personalInfo: {

View file

@ -1,11 +1,11 @@
"use client";
import { useRef, useState, useEffect } from "react";
import { motion, useMotionValue, animate } from "framer-motion";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card";
import { animate, motion, useMotionValue } from "framer-motion";
import { ChevronLeft, ChevronRight, Clock } from "lucide-react";
import { useEffect, useRef, useState } from "react";
interface CardData {
id: number;

View file

@ -1,8 +1,8 @@
"use client";
import React, { useState, useRef, useEffect } from "react";
import { motion, useMotionValue, useTransform, animate } from "framer-motion";
import { cn } from "@/lib/utils";
import { animate, motion, useMotionValue, useTransform } from "framer-motion";
import React, { useEffect, useRef, useState } from "react";
interface SliderProps {
min?: number;

View file

@ -2,34 +2,34 @@
import type React from "react";
import { motion, type Variants } from "framer-motion";
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
} from "recharts";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { motion, type Variants } from "framer-motion";
import {
Download,
Settings,
BarChart3,
Activity,
Users,
TrendingUp,
Clock,
Menu,
BarChart3,
ChevronRight,
TrendingDown,
Clock,
DollarSign,
Download,
Menu,
Percent,
Settings,
TrendingDown,
TrendingUp,
Users,
Zap,
} from "lucide-react";
import { useState } from "react";
import {
CartesianGrid,
Line,
LineChart,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
// ============================================================================
// TYPES & INTERFACES

View file

@ -1,25 +1,25 @@
"use client";
import { motion } from "framer-motion";
import { useState } from "react";
import {
TrendingUp,
TrendingDown,
DollarSign,
BarChart3,
ChevronRight,
Building2,
Activity,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogDescription,
} from "@/components/ui/dialog";
import { Badge } from "@/components/ui/badge";
import { motion } from "framer-motion";
import {
Activity,
BarChart3,
Building2,
ChevronRight,
DollarSign,
TrendingDown,
TrendingUp,
} from "lucide-react";
import { useState } from "react";
interface Stock {
id: string;

View file

@ -1,8 +1,8 @@
"use client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { motion } from "framer-motion";
import { useState } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
export function AnimatedTabs() {
const tabs = ["Account", "Password", "Settings"];

View file

@ -2,18 +2,9 @@
import type React from "react";
import { useMemo } from "react";
import { motion, type Variants } from "framer-motion";
import {
Area,
AreaChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
} from "recharts";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { motion, type Variants } from "framer-motion";
import {
CalendarDays,
Cloud,
@ -27,10 +18,18 @@ import {
Sun,
Sunrise,
Sunset,
Thermometer,
Umbrella,
Wind,
} from "lucide-react";
import { useMemo } from "react";
import {
Area,
AreaChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
} from "recharts";
type WeatherCondition = "sunny" | "rain" | "cloudy" | "storm";
@ -688,8 +687,8 @@ export function WeatherDashboard(): React.ReactElement {
color: "hsl(var(--foreground))",
fontWeight: 600,
}}
formatter={(value: number, key: string) => [
value.toString() + "°",
formatter={(value: any, key: any) => [
value?.toString() + "°",
key === "feelsLike" ? "Feels like" : "Temperature",
]}
/>

View file

@ -1,8 +1,8 @@
"use client";
import { useMemo, useState } from "react";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { cn } from "@/lib/utils";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import { useMemo, useState } from "react";
type ChartView = "monthly" | "yearly";

View file

@ -1,8 +1,8 @@
"use client";
import { motion, useMotionValue, useTransform, animate } from "framer-motion";
import { useEffect } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { animate, motion, useMotionValue, useTransform } from "framer-motion";
import { useEffect } from "react";
export function AnimatedProgress() {
const progress = useMotionValue(0);

View file

@ -1,8 +1,8 @@
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { AnimatePresence, motion } from "framer-motion";
import { ChevronDown } from "lucide-react";
import { useState } from "react";
const items = [
{

View file

@ -1,6 +1,6 @@
import Link from "next/link";
import Image from "next/image";
import { GithubIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
export function Footer() {
return (

View file

@ -1,8 +1,8 @@
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
import { Check } from "lucide-react";
import { useState } from "react";
export function AnimatedCheckbox() {
const [isChecked, setIsChecked] = useState(false);

View file

@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
import { useState } from "react";
const options = ["Daily", "Weekly", "Monthly", "Never"];

View file

@ -1,8 +1,8 @@
"use client";
import { AnimatePresence, motion } from "framer-motion";
import { Check, ChevronDown } from "lucide-react";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { ChevronDown, Check } from "lucide-react";
const options = ["React", "Vue", "Angular", "Svelte", "Next.js"];

View file

@ -1,8 +1,8 @@
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { useState } from "react";
const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const daysInMonth = Array.from({ length: 28 }, (_, i) => i + 1);

View file

@ -1,21 +1,21 @@
"use client";
import { useState } from "react";
import { motion, type Variants } from "framer-motion";
import {
Check,
ArrowRight,
ArrowLeft,
User,
MapPin,
Settings,
FileCheck,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import { motion, type Variants } from "framer-motion";
import {
ArrowLeft,
ArrowRight,
Check,
FileCheck,
MapPin,
Settings,
User,
} from "lucide-react";
import { useState } from "react";
// ============================================================================
// ANIMATION VARIANTS

View file

@ -1,5 +1,5 @@
import { use, useMemo, Suspense } from "react";
import { Star } from "lucide-react";
import { Suspense, use, useMemo } from "react";
const GITHUB_REPO = "moumen-soliman/uitripled";

Some files were not shown because too many files have changed in this diff Show more