Merge version_1 into main
Merge version_1 into main
This commit was merged in pull request #2.
This commit is contained in:
52
package.json
52
package.json
@@ -1,41 +1,25 @@
|
||||
{
|
||||
"name": "webild-components-2",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"name": "webild-components-2", "version": "0.1.0", "private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build --turbopack",
|
||||
"start": "next start",
|
||||
"lint": "eslint"
|
||||
"dev": "react-scripts start", "build": "react-scripts build", "start": "react-scripts start", "lint": "eslint", "test": "react-scripts test", "eject": "react-scripts eject"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gsap/react": "^2.1.2",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.4.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cobe": "^0.6.5",
|
||||
"embla-carousel-auto-scroll": "^8.6.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"gsap": "^3.13.0",
|
||||
"lenis": "^1.3.15",
|
||||
"lucide-react": "^0.555.0",
|
||||
"motion-number": "^1.0.0",
|
||||
"next": "16.0.7",
|
||||
"react": "19.2.1",
|
||||
"react-dom": "19.2.1",
|
||||
"react-fast-marquee": "^1.6.5",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"three": "^0.181.2"
|
||||
"@gsap/react": "^2.1.2", "@react-three/drei": "^10.7.7", "@react-three/fiber": "^9.4.0", "clsx": "^2.1.1", "cobe": "^0.6.5", "embla-carousel-auto-scroll": "^8.6.0", "embla-carousel-react": "^8.6.0", "gsap": "^3.13.0", "lenis": "^1.3.15", "lucide-react": "^0.555.0", "motion-number": "^1.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-fast-marquee": "^1.6.5", "react-scripts": "5.0.1", "tailwind-merge": "^3.4.0", "three": "^0.181.2", "web-vitals": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.0.7",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
"@eslint/eslintrc": "^3", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^9", "tailwindcss": "^4", "typescript": "^5"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app", "react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%", "not dead", "not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version", "last 1 firefox version", "last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
1237
public/index.html
Normal file
1237
public/index.html
Normal file
File diff suppressed because it is too large
Load Diff
23
src/App.tsx
Normal file
23
src/App.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { ThemeProvider } from './components/ui/ThemeProvider';
|
||||
import NavbarStyleApple from './components/navbar/NavbarStyleApple/NavbarStyleApple';
|
||||
import HeroBillboard from './components/sections/hero/HeroBillboard';
|
||||
import FeatureCardOne from './components/sections/feature/FeatureCardOne';
|
||||
import GalleryOne from './components/sections/gallery/GalleryOne';
|
||||
import PricingOne from './components/sections/pricing/PricingOne';
|
||||
import ContactTwo from './components/sections/contact/ContactTwo';
|
||||
import FooterStyleTwo from './components/footer/FooterStyleTwo/FooterStyleTwo';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
||||
<NavbarStyleApple />
|
||||
<HeroBillboard />
|
||||
<FeatureCardOne />
|
||||
<GalleryOne />
|
||||
<PricingOne />
|
||||
<ContactTwo />
|
||||
<FooterStyleTwo />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
51
src/components/footer/FooterStyleTwo/FooterStyleTwo.tsx
Normal file
51
src/components/footer/FooterStyleTwo/FooterStyleTwo.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
|
||||
const FooterStyleTwo = () => {
|
||||
return (
|
||||
<footer className="bg-gray-900 text-white">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="grid md:grid-cols-4 gap-8">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">Company</h3>
|
||||
<ul className="space-y-2">
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">About</a></li>
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Careers</a></li>
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Blog</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">Product</h3>
|
||||
<ul className="space-y-2">
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Features</a></li>
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Pricing</a></li>
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Documentation</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">Support</h3>
|
||||
<ul className="space-y-2">
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Help Center</a></li>
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Contact</a></li>
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Status</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">Legal</h3>
|
||||
<ul className="space-y-2">
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Privacy</a></li>
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Terms</a></li>
|
||||
<li><a href="#" className="text-gray-300 hover:text-white">Security</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-gray-800 pt-8 mt-8 text-center">
|
||||
<p className="text-gray-400">
|
||||
© 2024 Your Company. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default FooterStyleTwo;
|
||||
@@ -1,87 +1,23 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useCallback } from "react";
|
||||
import MobileMenu from "../mobileMenu/MobileMenu";
|
||||
import ButtonTextUnderline from "@/components/button/ButtonTextUnderline";
|
||||
import Logo from "../Logo";
|
||||
import { Plus } from "lucide-react";
|
||||
import { NavbarProps } from "@/types/navigation";
|
||||
import { useScrollState } from "./useScrollState";
|
||||
import { cls } from "@/lib/utils";
|
||||
|
||||
const SCROLL_THRESHOLD = 50;
|
||||
|
||||
const NavbarStyleApple = ({
|
||||
navItems,
|
||||
// logoSrc,
|
||||
// logoAlt = "",
|
||||
brandName = "Webild",
|
||||
}: NavbarProps) => {
|
||||
const isScrolled = useScrollState(SCROLL_THRESHOLD);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
const handleMenuToggle = useCallback(() => {
|
||||
setMenuOpen((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
const handleMobileNavClick = useCallback(() => {
|
||||
setMenuOpen(false);
|
||||
}, []);
|
||||
import React from 'react';
|
||||
|
||||
const NavbarStyleApple = () => {
|
||||
return (
|
||||
<nav
|
||||
className={cls(
|
||||
"fixed z-[1000] top-0 left-0 w-full transition-all duration-500 ease-in-out",
|
||||
isScrolled
|
||||
? "bg-background/80 backdrop-blur-sm h-15"
|
||||
: "bg-background/0 backdrop-blur-0 h-20"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center justify-between h-full w-content-width mx-auto">
|
||||
<div className="flex items-center transition-all duration-500 ease-in-out">
|
||||
<Logo brandName={brandName} />
|
||||
<nav className="bg-white dark:bg-gray-900 shadow">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between h-16">
|
||||
<div className="flex items-center">
|
||||
<img className="h-8 w-8" src="/images/logo-small-gradient.0ed287ce-1768918267834.svg" alt="Logo" />
|
||||
</div>
|
||||
<div className="hidden md:flex items-center space-x-8">
|
||||
<a href="#" className="text-gray-700 dark:text-gray-300 hover:text-blue-600">Home</a>
|
||||
<a href="#" className="text-gray-700 dark:text-gray-300 hover:text-blue-600">Features</a>
|
||||
<a href="#" className="text-gray-700 dark:text-gray-300 hover:text-blue-600">Pricing</a>
|
||||
<a href="#" className="text-gray-700 dark:text-gray-300 hover:text-blue-600">Contact</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="hidden md:flex items-center gap-6 transition-all duration-500 ease-in-out"
|
||||
role="navigation"
|
||||
>
|
||||
{navItems.map((item, index) => (
|
||||
<ButtonTextUnderline
|
||||
key={index}
|
||||
text={item.name}
|
||||
href={item.id}
|
||||
className="!text-base"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="flex md:hidden shrink-0 h-8 aspect-square rounded-theme bg-foreground items-center justify-center cursor-pointer"
|
||||
onClick={handleMenuToggle}
|
||||
aria-label="Toggle menu"
|
||||
aria-expanded={menuOpen}
|
||||
aria-controls="mobile-menu"
|
||||
>
|
||||
<Plus
|
||||
className={cls(
|
||||
"w-1/2 h-1/2 text-background transition-transform duration-300",
|
||||
menuOpen ? "rotate-45" : "rotate-0"
|
||||
)}
|
||||
strokeWidth={1.5}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<MobileMenu
|
||||
menuOpen={menuOpen}
|
||||
onMenuToggle={handleMenuToggle}
|
||||
navItems={navItems}
|
||||
onNavClick={handleMobileNavClick}
|
||||
/>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavbarStyleApple;
|
||||
export default NavbarStyleApple;
|
||||
85
src/components/sections/contact/ContactTwo.tsx
Normal file
85
src/components/sections/contact/ContactTwo.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import { Mail, Phone, MapPin } from 'lucide-react';
|
||||
|
||||
const ContactTwo = () => {
|
||||
return (
|
||||
<section className="py-20 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Get in Touch
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
Ready to start your project? Contact us today.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid lg:grid-cols-2 gap-12">
|
||||
<div>
|
||||
<div className="space-y-8">
|
||||
<div className="flex items-center">
|
||||
<Mail className="w-6 h-6 text-blue-600 mr-4" />
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Email</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">hello@example.com</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Phone className="w-6 h-6 text-blue-600 mr-4" />
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Phone</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">+1 (555) 123-4567</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<MapPin className="w-6 h-6 text-blue-600 mr-4" />
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Address</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">123 Main St, City, State 12345</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white dark:bg-gray-900 p-8 rounded-xl">
|
||||
<form className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Message
|
||||
</label>
|
||||
<textarea
|
||||
rows={4}
|
||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
|
||||
></textarea>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-blue-600 text-white py-3 px-6 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Send Message
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactTwo;
|
||||
@@ -1,171 +1,54 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
import { Rocket, Shield, Zap } from 'lucide-react';
|
||||
|
||||
import CardStack from "@/components/cardStack/CardStack";
|
||||
import MediaContent from "@/components/shared/MediaContent";
|
||||
import Button from "@/components/button/Button";
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
import { getButtonProps } from "@/lib/buttonUtils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ButtonConfig, GridVariant, CardAnimationType, TitleSegment } from "@/components/cardStack/types";
|
||||
|
||||
import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants";
|
||||
|
||||
type FeatureCard = {
|
||||
title: string;
|
||||
description: string;
|
||||
button?: ButtonConfig;
|
||||
} & (
|
||||
| {
|
||||
imageSrc: string;
|
||||
imageAlt?: string;
|
||||
videoSrc?: never;
|
||||
videoAriaLabel?: never;
|
||||
const FeatureCardOne = () => {
|
||||
const features = [
|
||||
{
|
||||
icon: <Rocket className="w-8 h-8 text-blue-600" />,
|
||||
title: 'Fast Performance',
|
||||
description: 'Optimized for speed and performance with modern web standards.'
|
||||
},
|
||||
{
|
||||
icon: <Shield className="w-8 h-8 text-green-600" />,
|
||||
title: 'Secure by Default',
|
||||
description: 'Built with security best practices and regular updates.'
|
||||
},
|
||||
{
|
||||
icon: <Zap className="w-8 h-8 text-yellow-600" />,
|
||||
title: 'Easy to Use',
|
||||
description: 'Simple API and comprehensive documentation for quick setup.'
|
||||
}
|
||||
| {
|
||||
videoSrc: string;
|
||||
videoAriaLabel?: string;
|
||||
imageSrc?: never;
|
||||
imageAlt?: never;
|
||||
}
|
||||
);
|
||||
|
||||
interface FeatureCardOneProps {
|
||||
features: FeatureCard[];
|
||||
carouselMode?: "auto" | "buttons";
|
||||
gridVariant: GridVariant;
|
||||
uniformGridCustomHeightClasses?: string;
|
||||
animationType: CardAnimationType;
|
||||
title: string;
|
||||
titleSegments?: TitleSegment[];
|
||||
description: string;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
textboxLayout: TextboxLayout;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
cardClassName?: string;
|
||||
mediaClassName?: string;
|
||||
textBoxTitleClassName?: string;
|
||||
textBoxTitleImageWrapperClassName?: string;
|
||||
textBoxTitleImageClassName?: string;
|
||||
textBoxDescriptionClassName?: string;
|
||||
cardTitleClassName?: string;
|
||||
cardDescriptionClassName?: string;
|
||||
cardButtonClassName?: string;
|
||||
cardButtonTextClassName?: string;
|
||||
gridClassName?: string;
|
||||
carouselClassName?: string;
|
||||
controlsClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
textBoxTagClassName?: string;
|
||||
textBoxButtonContainerClassName?: string;
|
||||
textBoxButtonClassName?: string;
|
||||
textBoxButtonTextClassName?: string;
|
||||
}
|
||||
|
||||
const FeatureCardOne = ({
|
||||
features,
|
||||
carouselMode = "buttons",
|
||||
gridVariant,
|
||||
uniformGridCustomHeightClasses,
|
||||
animationType,
|
||||
title,
|
||||
titleSegments,
|
||||
description,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
textboxLayout,
|
||||
useInvertedBackground,
|
||||
ariaLabel = "Feature section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
cardClassName = "",
|
||||
mediaClassName = "",
|
||||
textBoxTitleClassName = "",
|
||||
textBoxTitleImageWrapperClassName = "",
|
||||
textBoxTitleImageClassName = "",
|
||||
textBoxDescriptionClassName = "",
|
||||
cardTitleClassName = "",
|
||||
cardDescriptionClassName = "",
|
||||
cardButtonClassName = "",
|
||||
cardButtonTextClassName = "",
|
||||
gridClassName = "",
|
||||
carouselClassName = "",
|
||||
controlsClassName = "",
|
||||
textBoxClassName = "",
|
||||
textBoxTagClassName = "",
|
||||
textBoxButtonContainerClassName = "",
|
||||
textBoxButtonClassName = "",
|
||||
textBoxButtonTextClassName = "",
|
||||
}: FeatureCardOneProps) => {
|
||||
const theme = useTheme();
|
||||
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
|
||||
];
|
||||
|
||||
return (
|
||||
<CardStack
|
||||
mode={carouselMode}
|
||||
gridVariant={gridVariant}
|
||||
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
|
||||
animationType={animationType}
|
||||
|
||||
title={title}
|
||||
titleSegments={titleSegments}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
className={className}
|
||||
containerClassName={containerClassName}
|
||||
gridClassName={gridClassName}
|
||||
carouselClassName={carouselClassName}
|
||||
controlsClassName={controlsClassName}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={textBoxTitleClassName}
|
||||
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
|
||||
titleImageClassName={textBoxTitleImageClassName}
|
||||
descriptionClassName={textBoxDescriptionClassName}
|
||||
tagClassName={textBoxTagClassName}
|
||||
buttonContainerClassName={textBoxButtonContainerClassName}
|
||||
buttonClassName={textBoxButtonClassName}
|
||||
buttonTextClassName={textBoxButtonTextClassName}
|
||||
ariaLabel={ariaLabel}
|
||||
>
|
||||
{features.map((feature, index) => (
|
||||
<div
|
||||
key={`${feature.title}-${index}`}
|
||||
className={cls("card flex flex-col gap-4 p-4 rounded-theme-capped min-h-0 h-full", cardClassName)}
|
||||
>
|
||||
<MediaContent
|
||||
imageSrc={feature.imageSrc}
|
||||
videoSrc={feature.videoSrc}
|
||||
imageAlt={feature.imageAlt || "Feature image"}
|
||||
videoAriaLabel={feature.videoAriaLabel || "Feature video"}
|
||||
imageClassName={cls("relative z-1 min-h-0 h-full", mediaClassName)}
|
||||
/>
|
||||
<div className="relative z-1 flex flex-col gap-1">
|
||||
<h3 className={cls("text-2xl font-medium", shouldUseLightText && "text-background", cardTitleClassName)}>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p className={cls("text-sm leading-[1.1]", shouldUseLightText ? "text-background" : "text-foreground", cardDescriptionClassName)}>
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
{feature.button && (
|
||||
<Button {...getButtonProps(feature.button, 0, theme.defaultButtonVariant, cls("w-full", cardButtonClassName), cardButtonTextClassName)} />
|
||||
)}
|
||||
<section className="py-20 bg-white dark:bg-gray-900">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Why Choose Our Components?
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
Everything you need to build modern web applications.
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</CardStack>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{features.map((feature, index) => (
|
||||
<div key={index} className="bg-gray-50 dark:bg-gray-800 p-8 rounded-xl text-center">
|
||||
<div className="flex justify-center mb-4">
|
||||
{feature.icon}
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
{feature.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
FeatureCardOne.displayName = "FeatureCardOne";
|
||||
|
||||
export default FeatureCardOne;
|
||||
export default FeatureCardOne;
|
||||
38
src/components/sections/gallery/GalleryOne.tsx
Normal file
38
src/components/sections/gallery/GalleryOne.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
|
||||
const GalleryOne = () => {
|
||||
const images = [
|
||||
'/images/courses-box-1.e8498908-1768918267950.png',
|
||||
'/images/courses-box-2.2e36a41b-1768918267948.png',
|
||||
'/images/courses.aee168f4-1768918267985.png',
|
||||
'/images/256-1768918267936.png'
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-20 bg-gray-50 dark:bg-gray-800">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Gallery
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
Showcase of our work and components in action.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{images.map((src, index) => (
|
||||
<div key={index} className="bg-white dark:bg-gray-700 rounded-lg overflow-hidden shadow-lg">
|
||||
<img
|
||||
src={src}
|
||||
alt={`Gallery item ${index + 1}`}
|
||||
className="w-full h-48 object-cover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default GalleryOne;
|
||||
@@ -1,93 +1,29 @@
|
||||
"use client";
|
||||
import React from 'react';
|
||||
|
||||
import TextBox from "@/components/Textbox";
|
||||
import MediaContent from "@/components/shared/MediaContent";
|
||||
import { cls } from "@/lib/utils";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ButtonConfig } from "@/types/button";
|
||||
|
||||
interface HeroBillboardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
imageSrc?: string;
|
||||
videoSrc?: string;
|
||||
imageAlt?: string;
|
||||
videoAriaLabel?: string;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
titleClassName?: string;
|
||||
descriptionClassName?: string;
|
||||
tagClassName?: string;
|
||||
buttonContainerClassName?: string;
|
||||
buttonClassName?: string;
|
||||
buttonTextClassName?: string;
|
||||
mediaWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
}
|
||||
|
||||
const HeroBillboard = ({
|
||||
title,
|
||||
description,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
imageSrc,
|
||||
videoSrc,
|
||||
imageAlt = "",
|
||||
videoAriaLabel = "Hero video",
|
||||
ariaLabel = "Hero section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
textBoxClassName = "",
|
||||
titleClassName = "",
|
||||
descriptionClassName = "",
|
||||
tagClassName = "",
|
||||
buttonContainerClassName = "",
|
||||
buttonClassName = "",
|
||||
buttonTextClassName = "",
|
||||
mediaWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
}: HeroBillboardProps) => {
|
||||
const HeroBillboard = () => {
|
||||
return (
|
||||
<section
|
||||
aria-label={ariaLabel}
|
||||
className={cls("w-full py-hero-page-padding", className)}
|
||||
>
|
||||
<div className={cls("w-content-width mx-auto flex flex-col gap-14 md:gap-15", containerClassName)}>
|
||||
<TextBox
|
||||
title={title}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
className={cls("flex flex-col gap-3 md:gap-1", textBoxClassName)}
|
||||
titleClassName={cls("text-6xl font-medium text-balance", titleClassName)}
|
||||
descriptionClassName={cls("text-base md:text-lg leading-[1.2]", descriptionClassName)}
|
||||
tagClassName={cls("px-3 py-1 text-sm rounded-theme card text-foreground inline-flex items-center gap-2 mb-3", tagClassName)}
|
||||
buttonContainerClassName={cls("flex gap-4 mt-3", buttonContainerClassName)}
|
||||
buttonClassName={buttonClassName}
|
||||
buttonTextClassName={buttonTextClassName}
|
||||
center={true}
|
||||
/>
|
||||
<div className={cls("w-full overflow-hidden rounded-theme-capped card p-4", mediaWrapperClassName)}>
|
||||
<MediaContent
|
||||
imageSrc={imageSrc}
|
||||
videoSrc={videoSrc}
|
||||
imageAlt={imageAlt}
|
||||
videoAriaLabel={videoAriaLabel}
|
||||
imageClassName={cls("z-1", imageClassName)}
|
||||
/>
|
||||
<section className="bg-gradient-to-br from-blue-50 to-purple-50 dark:from-gray-900 dark:to-gray-800">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20">
|
||||
<div className="text-center">
|
||||
<h1 className="text-5xl font-bold text-gray-900 dark:text-white mb-6">
|
||||
Build Beautiful Components
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-8 max-w-3xl mx-auto">
|
||||
Create stunning web applications with our modern component library.
|
||||
Fast, reliable, and beautiful.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<button className="bg-blue-600 text-white px-8 py-3 rounded-lg hover:bg-blue-700 transition-colors">
|
||||
Get Started
|
||||
</button>
|
||||
<button className="bg-white text-gray-900 px-8 py-3 rounded-lg border border-gray-300 hover:bg-gray-50 transition-colors">
|
||||
Learn More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
HeroBillboard.displayName = "HeroBillboard";
|
||||
|
||||
export default HeroBillboard;
|
||||
76
src/components/sections/pricing/PricingOne.tsx
Normal file
76
src/components/sections/pricing/PricingOne.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import { Check } from 'lucide-react';
|
||||
|
||||
const PricingOne = () => {
|
||||
const plans = [
|
||||
{
|
||||
name: 'Starter',
|
||||
price: '$9',
|
||||
period: '/month',
|
||||
features: ['Up to 5 projects', 'Basic support', 'Core components']
|
||||
},
|
||||
{
|
||||
name: 'Pro',
|
||||
price: '$29',
|
||||
period: '/month',
|
||||
features: ['Unlimited projects', 'Priority support', 'All components', 'Custom themes'],
|
||||
popular: true
|
||||
},
|
||||
{
|
||||
name: 'Enterprise',
|
||||
price: '$99',
|
||||
period: '/month',
|
||||
features: ['Unlimited everything', '24/7 support', 'Custom development', 'SLA guarantee']
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="py-20 bg-white dark:bg-gray-900">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">
|
||||
Choose Your Plan
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
Simple, transparent pricing for teams of all sizes.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
{plans.map((plan, index) => (
|
||||
<div key={index} className={`bg-gray-50 dark:bg-gray-800 rounded-xl p-8 ${plan.popular ? 'ring-2 ring-blue-600' : ''}`}>
|
||||
{plan.popular && (
|
||||
<span className="bg-blue-600 text-white px-3 py-1 rounded-full text-sm font-medium mb-4 inline-block">
|
||||
Most Popular
|
||||
</span>
|
||||
)}
|
||||
<h3 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
{plan.name}
|
||||
</h3>
|
||||
<div className="mb-6">
|
||||
<span className="text-4xl font-bold text-gray-900 dark:text-white">{plan.price}</span>
|
||||
<span className="text-gray-600 dark:text-gray-300">{plan.period}</span>
|
||||
</div>
|
||||
<ul className="space-y-3 mb-8">
|
||||
{plan.features.map((feature, featureIndex) => (
|
||||
<li key={featureIndex} className="flex items-center">
|
||||
<Check className="w-5 h-5 text-green-600 mr-3" />
|
||||
<span className="text-gray-600 dark:text-gray-300">{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button className={`w-full py-3 px-6 rounded-lg font-medium transition-colors ${
|
||||
plan.popular
|
||||
? 'bg-blue-600 text-white hover:bg-blue-700'
|
||||
: 'bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-white hover:bg-gray-300 dark:hover:bg-gray-600'
|
||||
}`}>
|
||||
Get Started
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PricingOne;
|
||||
73
src/components/ui/ThemeProvider.tsx
Normal file
73
src/components/ui/ThemeProvider.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
|
||||
type Theme = 'dark' | 'light' | 'system';
|
||||
|
||||
type ThemeProviderProps = {
|
||||
children: React.ReactNode;
|
||||
defaultTheme?: Theme;
|
||||
storageKey?: string;
|
||||
};
|
||||
|
||||
type ThemeProviderState = {
|
||||
theme: Theme;
|
||||
setTheme: (theme: Theme) => void;
|
||||
};
|
||||
|
||||
const initialState: ThemeProviderState = {
|
||||
theme: 'system',
|
||||
setTheme: () => null,
|
||||
};
|
||||
|
||||
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
||||
|
||||
export function ThemeProvider({
|
||||
children,
|
||||
defaultTheme = 'system',
|
||||
storageKey = 'vite-ui-theme',
|
||||
...props
|
||||
}: ThemeProviderProps) {
|
||||
const [theme, setTheme] = useState<Theme>(
|
||||
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement;
|
||||
|
||||
root.classList.remove('light', 'dark');
|
||||
|
||||
if (theme === 'system') {
|
||||
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
.matches
|
||||
? 'dark'
|
||||
: 'light';
|
||||
|
||||
root.classList.add(systemTheme);
|
||||
return;
|
||||
}
|
||||
|
||||
root.classList.add(theme);
|
||||
}, [theme]);
|
||||
|
||||
const value = {
|
||||
theme,
|
||||
setTheme: (theme: Theme) => {
|
||||
localStorage.setItem(storageKey, theme);
|
||||
setTheme(theme);
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProviderContext.Provider {...props} value={value}>
|
||||
{children}
|
||||
</ThemeProviderContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useTheme = () => {
|
||||
const context = useContext(ThemeProviderContext);
|
||||
|
||||
if (context === undefined)
|
||||
throw new Error('useTheme must be used within a ThemeProvider');
|
||||
|
||||
return context;
|
||||
};
|
||||
17
src/index.css
Normal file
17
src/index.css
Normal file
@@ -0,0 +1,17 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
13
src/index.tsx
Normal file
13
src/index.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
Reference in New Issue
Block a user