@allbluecn/web-app 0.17.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/src/components/landing/landing-page.tsx +20 -0
  3. package/src/lib/changelog/sdk-versions.ts +1 -1
  4. package/src/paraglide/README.md +1 -1
  5. package/src/paraglide/messages/_index.js +0 -48
  6. package/src/paraglide/messages/en.js +0 -15
  7. package/src/paraglide/messages/zh.js +0 -15
  8. package/src/routes/_nav/prd/index.tsx +1 -1
  9. package/src/routes/_nav/tags/index.tsx +1 -1
  10. package/src/routes/_public/index/route.tsx +2 -6
  11. package/src/server/middlewares/paraglide-locale.ts +39 -0
  12. package/src/start.ts +4 -1
  13. package/src/styles/globals.css +2 -3
  14. package/src/__tests__/bench/baselines/format-timestamp.json +0 -44
  15. package/src/assets/logo/logo.tsx +0 -70
  16. package/src/components/shadcn-space/animations/marquee.tsx +0 -139
  17. package/src/components/shadcn-space/blocks/about-us-01/about-us.tsx +0 -136
  18. package/src/components/shadcn-space/blocks/about-us-01/index.tsx +0 -62
  19. package/src/components/shadcn-space/blocks/cta-01/cta.tsx +0 -74
  20. package/src/components/shadcn-space/blocks/faq-01/faq.tsx +0 -100
  21. package/src/components/shadcn-space/blocks/footer-01/footer.tsx +0 -232
  22. package/src/components/shadcn-space/blocks/hero-01/brand-slider.tsx +0 -79
  23. package/src/components/shadcn-space/blocks/hero-01/header.tsx +0 -272
  24. package/src/components/shadcn-space/blocks/hero-01/hero.tsx +0 -111
  25. package/src/components/shadcn-space/blocks/hero-01/index.tsx +0 -127
  26. package/src/components/shadcn-space/blocks/portfolio-01/portfolio.tsx +0 -149
  27. package/src/components/shadcn-space/blocks/pricing-01/pricing.tsx +0 -187
  28. package/src/components/shadcn-space/blocks/pricing-02/pricing.tsx +0 -213
  29. package/src/components/shadcn-space/blocks/services-01/services.tsx +0 -178
  30. package/src/components/shadcn-space/blocks/team-01/team.tsx +0 -166
@@ -1,272 +0,0 @@
1
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
- // AllBlue - 理想之海
3
- // Copyright (C) 2026 AllBlue Contributors
4
- //
5
- // This program is free software: you can redistribute it and/or modify
6
- // it under the terms of the GNU Affero General Public License as published by
7
- // the Free Software Foundation, either version 3 of the License, or
8
- // (at your option) any later version.
9
- //
10
- // This program is distributed in the hope that it will be useful,
11
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- // GNU Affero General Public License for more details.
14
- //
15
- // You should have received a copy of the GNU Affero General Public License
16
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
-
18
-
19
- import { useState, useEffect, useCallback } from "react";
20
- import { Sheet, SheetContent, SheetTrigger, SheetTitle, SheetClose } from "@/components/ui/sheet";
21
- import { NavigationMenu, NavigationMenuItem, NavigationMenuLink, NavigationMenuList } from "@/components/ui/navigation-menu";
22
- import { cn } from "@/lib/utils";
23
- import { Menu, X } from 'lucide-react';
24
- import { Icon } from "@iconify/react";
25
- import { Button } from "@/components/ui/button";
26
- import { m } from "motion/react";
27
- import { InteractiveHoverButton } from "@/components/magicui/interactive-hover-button";
28
- import { useSession } from "@/lib/auth/auth-client";
29
- import { useRouter } from "@tanstack/react-router";
30
- import { ThemeToggle } from "@/components/shared-kit/theme/theme-toggle";
31
- import { AnimatedThemeToggler } from '@/components/magicui/animated-theme-toggler'
32
-
33
- export type NavigationSection = {
34
- title: string;
35
- href: string;
36
- isActive?: boolean;
37
- };
38
-
39
- type HeaderProps = {
40
- navigationData: NavigationSection[];
41
- className?: string;
42
- };
43
-
44
- const CollaborateButton = ({ className, isMobile = false }: { className?: string; isMobile?: boolean }) => {
45
- const { data: session } = useSession();
46
- const router = useRouter();
47
-
48
- const isLoggedIn = !!session?.user;
49
-
50
- const handleClick = async () => {
51
- if (isLoggedIn) {
52
- await router.navigate({ to: "/dashboard" });
53
- } else {
54
- await router.navigate({ to: "/login" });
55
- }
56
- };
57
-
58
- if (isMobile) {
59
- return (
60
- <Button
61
- onClick={handleClick}
62
- className={cn("w-full text-lg font-semibold py-6", className)}
63
- >
64
- {isLoggedIn ? "Go to Workspace" : "Get Started / Sign In"}
65
- </Button>
66
- );
67
- }
68
-
69
- return (
70
- <div className="flex items-center gap-2">
71
- <InteractiveHoverButton
72
- onClick={handleClick}
73
- className={cn(className)}
74
- >
75
- {isLoggedIn ? "Go to Workspace" : "Get Started / Sign In"}
76
- </InteractiveHoverButton>
77
- <div className="mr-1">
78
- <ThemeToggle />
79
- </div>
80
- </div>
81
- );
82
-
83
- };
84
-
85
- const Header = ({ navigationData, className }: HeaderProps) => {
86
- const [sticky, setSticky] = useState(false);
87
- const [isOpen, setIsOpen] = useState(false);
88
-
89
- const handleScroll = useCallback(() => {
90
- setSticky(window.scrollY >= 50);
91
- }, []);
92
-
93
- const handleResize = useCallback(() => {
94
- if (window.innerWidth >= 768) setIsOpen(false);
95
- }, []);
96
-
97
- useEffect(() => {
98
- window.addEventListener("scroll", handleScroll);
99
- window.addEventListener("resize", handleResize);
100
-
101
- return () => {
102
- window.removeEventListener("scroll", handleScroll);
103
- window.removeEventListener("resize", handleResize);
104
- };
105
- }, [handleScroll, handleResize]);
106
-
107
- return (
108
- <m.header
109
- initial={{ opacity: 0, y: -32 }}
110
- whileInView={{ opacity: 1, y: 0 }}
111
- viewport={{ once: true }}
112
- transition={{ duration: 0.7 }}
113
- className={cn(
114
- "inset-x-0 z-50 px-4 flex items-center justify-center sticky top-0 h-20",
115
- className,
116
- )}
117
- >
118
- <div
119
- className={cn(
120
- "w-full max-w-6xl flex items-center h-fit justify-between gap-3.5 lg:gap-6 transition-all duration-500",
121
- sticky
122
- ? "p-2.5 bg-background/60 backdrop-blur-lg border border-border/40 shadow-2xl shadow-primary/5 rounded-full"
123
- : "bg-transparent border-transparent",
124
- )}
125
- >
126
- {/* Logo */}
127
- <div>
128
- <a href="#">
129
- <img
130
- src="/logo/allblue-logo-text-dark.svg"
131
- alt="AllBlue"
132
- className="h-6 dark:hidden"
133
- />
134
- <img
135
- src="/logo/allblue-logo-text-light.svg"
136
- alt="AllBlue"
137
- className="hidden h-6 dark:block"
138
- />
139
- </a>
140
- </div>
141
-
142
- {/* Desktop Navigation */}
143
- <div>
144
- <NavigationMenu className="max-lg:hidden bg-muted p-0.5 rounded-full">
145
- <NavigationMenuList className="flex gap-0">
146
- {navigationData.map((navItem) => (
147
- <NavigationMenuItem key={navItem.title}>
148
- <NavigationMenuLink
149
- href={navItem.href}
150
- className={cn("px-2 lg:px-4 py-2 text-sm font-medium rounded-full text-muted-foreground hover:text-foreground hover:bg-background outline outline-transparent hover:outline-border hover:shadow-xs transition tracking-normal", navItem.isActive ? "bg-background text-foreground" : "")}
151
- >
152
- {navItem.title}
153
- </NavigationMenuLink>
154
- </NavigationMenuItem>
155
- ))}
156
- </NavigationMenuList>
157
- </NavigationMenu>
158
- </div>
159
-
160
- {/* Desktop CTA */}
161
- <div className="flex gap-4">
162
- <CollaborateButton className="hidden lg:flex" />
163
-
164
- <div className="lg:hidden">
165
- <Sheet open={isOpen} onOpenChange={setIsOpen}>
166
- <SheetTrigger id="mobile-menu-trigger">
167
- <span className="rounded-full border border-border p-2 block">
168
- <Menu
169
- width={20}
170
- height={20}
171
- />
172
- <span className="sr-only">Menu</span>
173
- </span>
174
- </SheetTrigger>
175
-
176
- <SheetContent
177
- showCloseButton={false}
178
- side="right"
179
- className="w-full sm:w-96 p-0 border-l-0"
180
- >
181
- <div className="flex items-center justify-between p-6">
182
- <a href="#">
183
- <img
184
- src="/logo/allblue-logo-text-dark.svg"
185
- alt="AllBlue"
186
- className="h-5 dark:hidden"
187
- />
188
- <img
189
- src="/logo/allblue-logo-text-light.svg"
190
- alt="AllBlue"
191
- className="hidden h-5 dark:block"
192
- />
193
- </a>
194
- <SheetClose id="mobile-menu-close">
195
- <span className="rounded-full border border-border p-2.5 block">
196
- <X width={16} height={16} />
197
- </span>
198
- </SheetClose>
199
- </div>
200
-
201
- <div className="flex flex-col gap-12 px-6 pb-6 overflow-y-auto">
202
- <div className="flex flex-col gap-8">
203
- <SheetTitle className="sr-only">Menu</SheetTitle>
204
- <NavigationMenu
205
- orientation="vertical"
206
- className="items-start flex-none"
207
- >
208
- <NavigationMenuList className="flex flex-col items-start gap-3">
209
- {navigationData.map((item) => (
210
- <NavigationMenuItem key={item.title}>
211
- <NavigationMenuLink
212
- href={item.href}
213
- className={cn(
214
- "group/nav flex items-center text-2xl font-semibold tracking-tight transition-all p-0 hover:bg-transparent focus:bg-transparent data-active:bg-transparent data-[state=open]:bg-transparent",
215
- item.isActive
216
- ? "text-primary"
217
- : "text-muted-foreground hover:text-foreground hover:translate-x-2",
218
- )}
219
- >
220
- <div
221
- className={cn(
222
- "h-0.5 bg-primary transition-all duration-300 overflow-hidden",
223
- item.isActive
224
- ? "w-4 mr-2 opacity-100"
225
- : "w-0 opacity-0 group-hover/nav:w-4 group-hover/nav:mr-2 group-hover/nav:opacity-100",
226
- )}
227
- />
228
- {item.title}
229
- </NavigationMenuLink>
230
- </NavigationMenuItem>
231
- ))}
232
- </NavigationMenuList>
233
- </NavigationMenu>
234
-
235
- <div className="w-fit">
236
- <CollaborateButton isMobile />
237
- </div>
238
- </div>
239
-
240
- <div className="mt-auto flex flex-col gap-4">
241
- <div className="flex gap-3">
242
- {[
243
- "lucide:dribbble",
244
- "lucide:instagram",
245
- "lucide:twitter",
246
- "lucide:linkedin",
247
- ].map((icon) => (
248
- <a
249
- key={icon}
250
- href="#"
251
- className="flex items-center justify-center rounded-full outline outline-border hover:bg-muted transition p-3 shadow-xs"
252
- >
253
- <Icon icon={icon} width={16} height={16} />
254
- </a>
255
- ))}
256
- </div>
257
-
258
- <p className="text-sm text-muted-foreground">
259
- © 2026 Shadcn Space
260
- </p>
261
- </div>
262
- </div>
263
- </SheetContent>
264
- </Sheet>
265
- </div>
266
- </div>
267
- </div>
268
- </m.header>
269
- );
270
- };
271
-
272
- export default Header;
@@ -1,111 +0,0 @@
1
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
- // AllBlue - 理想之海
3
- // Copyright (C) 2026 AllBlue Contributors
4
- //
5
- // This program is free software: you can redistribute it and/or modify
6
- // it under the terms of the GNU Affero General Public License as published by
7
- // the Free Software Foundation, either version 3 of the License, or
8
- // (at your option) any later version.
9
- //
10
- // This program is distributed in the hope that it will be useful,
11
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- // GNU Affero General Public License for more details.
14
- //
15
- // You should have received a copy of the GNU Affero General Public License
16
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
-
18
- import { m } from "motion/react";
19
- import { Button } from "@/components/ui/button";
20
- import { ArrowUpRight } from "lucide-react";
21
- export type AvatarList = {
22
- image: string;
23
- };
24
- type HeroSectionProps = {
25
- avatarList: AvatarList[];
26
- };
27
- function HeroSection({ avatarList }: HeroSectionProps) {
28
- return (
29
- <section>
30
- <div className="w-full h-full relative">
31
- <div className="relative w-full pt-0 md:pt-20 pb-6 md:pb-10 before:absolute before:w-full before:h-full before:bg-linear-to-r before:from-sky-100 before:via-white before:to-amber-100 before:rounded-full before:top-24 before:blur-3xl before:-z-10 dark:before:from-slate-800 dark:before:via-black dark:before:to-stone-700 dark:before:rounded-full dark:before:blur-3xl dark:before:-z-10">
32
- <div className="container mx-auto relative z-10">
33
- <div className="flex flex-col max-w-5xl mx-auto gap-8">
34
- <div className="relative flex flex-col text-center items-center sm:gap-6 gap-4">
35
- <m.h1
36
- initial={{ opacity: 0, y: 32 }}
37
- animate={{ opacity: 1, y: 0 }}
38
- transition={{ duration: 0.8, ease: [0.21, 0.47, 0.32, 0.98] }}
39
- className="lg:text-8xl md:text-7xl text-5xl font-medium leading-14 md:leading-20 lg:leading-24"
40
- >
41
- Building bold brands with{" "}
42
- <span
43
- className="font-serif italic tracking-tight"
44
- >
45
- thoughtful design
46
- </span>
47
- </m.h1>
48
- <m.p
49
- initial={{ opacity: 0, y: 24 }}
50
- animate={{ opacity: 1, y: 0 }}
51
- transition={{ duration: 0.8, delay: 0.15, ease: [0.21, 0.47, 0.32, 0.98] }}
52
- className="text-base font-normal max-w-2xl text-muted-foreground"
53
- >
54
- At shadcn space, we help small startups tackle the world's
55
- biggest challenges with tailored solutions, guiding you from
56
- strategy to success in a competitive market.
57
- </m.p>
58
- </div>
59
- <m.div
60
- initial={{ opacity: 0, y: 24 }}
61
- animate={{ opacity: 1, y: 0 }}
62
- transition={{ duration: 0.8, delay: 0.3, ease: [0.21, 0.47, 0.32, 0.98] }}
63
- className="flex items-center flex-col md:flex-row justify-center gap-8"
64
- >
65
- <Button className="relative h-12 p-1 ps-6 pe-14 w-fit overflow-hidden rounded-full transition-all duration-500 hover:ps-14 hover:pe-6 group">
66
- <span className="relative z-10 transition-all duration-500">Get Started</span>
67
- <span className="absolute right-1 w-10 h-10 bg-background text-foreground rounded-full flex items-center justify-center transition-all duration-500 group-hover:right-[calc(100%-44px)] group-hover:rotate-45">
68
- <ArrowUpRight className="size-4" />
69
- </span>
70
- </Button>
71
- <div className="flex items-center sm:gap-7 gap-3">
72
- <ul className="avatar flex flex-row items-center">
73
- {avatarList.map((avatar, index) => (
74
- <li key={index} className="-mr-2 z-1 avatar-hover:ml-2">
75
- <img
76
- src={avatar.image}
77
- alt="Avatar"
78
- width={40}
79
- height={40}
80
- className="rounded-full border-2 border-white"
81
- />
82
- </li>
83
- ))}
84
- </ul>
85
- <div className="gap-1 flex flex-col items-start">
86
- <div className="flex gap-1">
87
- {Array.from({ length: 5 }).map((_, index) => (
88
- <img
89
- key={index}
90
- src="https://images.shadcnspace.com/assets/svgs/icon-star.svg"
91
- alt="star"
92
- width={16}
93
- height={16}
94
- className="h-4 w-4"
95
- />
96
- ))}
97
- </div>
98
- <p className="sm:text-sm text-xs font-normal text-muted-foreground">
99
- Trusted by 1000+ clients
100
- </p>
101
- </div>
102
- </div>
103
- </m.div>
104
- </div>
105
- </div>
106
- </div>
107
- </div>
108
- </section>
109
- );
110
- }
111
- export default HeroSection;
@@ -1,127 +0,0 @@
1
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
- // AllBlue - 理想之海
3
- // Copyright (C) 2026 AllBlue Contributors
4
- //
5
- // This program is free software: you can redistribute it and/or modify
6
- // it under the terms of the GNU Affero General Public License as published by
7
- // the Free Software Foundation, either version 3 of the License, or
8
- // (at your option) any later version.
9
- //
10
- // This program is distributed in the hope that it will be useful,
11
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- // GNU Affero General Public License for more details.
14
- //
15
- // You should have received a copy of the GNU Affero General Public License
16
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
-
18
- import HeroSection from "@/components/shadcn-space/blocks/hero-01/hero";
19
- import type { NavigationSection } from "@/components/shadcn-space/blocks/hero-01/header";
20
- import Header from "@/components/shadcn-space/blocks/hero-01/header";
21
- import BrandSlider, { BrandList } from "@/components/shadcn-space/blocks/hero-01/brand-slider";
22
- import type { AvatarList } from "@/components/shadcn-space/blocks/hero-01/hero";
23
-
24
- import Pricing from "@/components/shadcn-space/blocks/pricing-01/pricing";
25
- import AboutAndStats01 from "@/components/shadcn-space/blocks/about-us-01"
26
- import Services from "@/components/shadcn-space/blocks/services-01/services";
27
- import CTA from "@/components/shadcn-space/blocks/cta-01/cta";
28
- import Faq from "@/components/shadcn-space/blocks/faq-01/faq"
29
- import Footer from "@/components/shadcn-space/blocks/footer-01/footer";
30
- import Team from "@/components/shadcn-space/blocks/team-01/team";
31
- import Portfolio from "@/components/shadcn-space/blocks/portfolio-01/portfolio";
32
-
33
-
34
- export default function AgencyHeroSection() {
35
- const avatarList: AvatarList[] = [
36
- {
37
- image: "https://images.shadcnspace.com/assets/profiles/user-1.jpg",
38
- },
39
- {
40
- image: "https://images.shadcnspace.com/assets/profiles/user-2.jpg",
41
- },
42
- {
43
- image: "https://images.shadcnspace.com/assets/profiles/user-3.jpg",
44
- },
45
- {
46
- image: "https://images.shadcnspace.com/assets/profiles/user-5.jpg",
47
- },
48
- ];
49
-
50
- const navigationData: NavigationSection[] = [
51
- {
52
- title: "Home",
53
- href: "#",
54
- isActive: true,
55
- },
56
- {
57
- title: "About us",
58
- href: "#",
59
- },
60
- {
61
- title: "Services",
62
- href: "#",
63
- },
64
- {
65
- title: "Team",
66
- href: "#",
67
- },
68
- {
69
- title: "Pricing",
70
- href: "#pricing",
71
- },
72
- {
73
- title: "Awards",
74
- href: "#",
75
- },
76
- ];
77
-
78
- const brandList: BrandList[] = [
79
- {
80
- image: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-1.svg",
81
- lightimg: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-light-1.svg",
82
- name: "Brand 1",
83
- },
84
- {
85
- image: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-2.svg",
86
- lightimg: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-light-2.svg",
87
- name: "Brand 2",
88
- },
89
- {
90
- image: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-3.svg",
91
- lightimg: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-light-3.svg",
92
- name: "Brand 3",
93
- },
94
- {
95
- image: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-4.svg",
96
- lightimg: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-light-4.svg",
97
- name: "Brand 4",
98
- },
99
- {
100
- image: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-5.svg",
101
- lightimg: "https://images.shadcnspace.com/assets/brand-logo/logoipsum-light-5.svg",
102
- name: "Brand 5",
103
- },
104
- ];
105
-
106
- return (
107
- <div className="relative">
108
- <Header navigationData={navigationData} />
109
- <main>
110
- <HeroSection avatarList={avatarList} />
111
- <BrandSlider brandList={brandList} />
112
- <AboutAndStats01 />
113
- <Portfolio />
114
- <Services />
115
- <Team />
116
-
117
- <div id="pricing">
118
- <Pricing />
119
- </div>
120
-
121
- <Faq />
122
- <CTA />
123
- <Footer />
124
- </main>
125
- </div>
126
- );
127
- }
@@ -1,149 +0,0 @@
1
- // SPDX-License-Identifier: AGPL-3.0-or-later
2
- // AllBlue - 理想之海
3
- // Copyright (C) 2026 AllBlue Contributors
4
- //
5
- // This program is free software: you can redistribute it and/or modify
6
- // it under the terms of the GNU Affero General Public License as published by
7
- // the Free Software Foundation, either version 3 of the License, or
8
- // (at your option) any later version.
9
- //
10
- // This program is distributed in the hope that it will be useful,
11
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- // GNU Affero General Public License for more details.
14
- //
15
- // You should have received a copy of the GNU Affero General Public License
16
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
-
18
-
19
- ;
20
- import { Badge } from "@/components/ui/badge";
21
- import { Card, CardContent } from "@/components/ui/card";
22
- import { m } from "motion/react";
23
-
24
- type PortfolioData = {
25
- portfolio_image: string;
26
- portfolio_title: string;
27
- portfolio_tags: string[];
28
- };
29
-
30
- const portfolioData: PortfolioData[] = [
31
- {
32
- portfolio_image:
33
- "https://images.shadcnspace.com/assets/portfolio/flowbank.webp",
34
- portfolio_title: "Flowbank",
35
- portfolio_tags: ["UX Research", "Interface Design"],
36
- },
37
- {
38
- portfolio_image:
39
- "https://images.shadcnspace.com/assets/portfolio/academy.webp",
40
- portfolio_title: "Academy.co",
41
- portfolio_tags: ["Product Design", "Interection Design"],
42
- },
43
- {
44
- portfolio_image:
45
- "https://images.shadcnspace.com/assets/portfolio/genome.webp",
46
- portfolio_title: "Genome",
47
- portfolio_tags: ["Brand identity design", "UX Research"],
48
- },
49
- {
50
- portfolio_image:
51
- "https://images.shadcnspace.com/assets/portfolio/hotto.webp",
52
- portfolio_title: "Hotto",
53
- portfolio_tags: ["Visual Storytelling", "Web & Mobile Design"],
54
- },
55
- ];
56
-
57
- const Portfolio = () => {
58
- const cardVariants = {
59
- hidden: {
60
- opacity: 0,
61
- y: 80,
62
- },
63
- visible: (index: number) => ({
64
- opacity: 1,
65
- y: 0,
66
- transition: {
67
- delay: index * 0.3,
68
- duration: 0.6,
69
- },
70
- }),
71
- };
72
-
73
- return (
74
- <section className="bg-background py-10">
75
- <div className="max-w-7xl mx-auto px-4 lg:px-8 xl:px-16">
76
- <div className="flex flex-col gap-8 sm:gap-12 justify-center items-center w-full">
77
- {/* Heading */}
78
- <div className="flex flex-col gap-4 justify-center items-center animate-in fade-in slide-in-from-top-8 duration-700 ease-in-out">
79
- {/* Badge */}
80
- <Badge
81
- variant={"outline"}
82
- className="py-1 px-3 text-sm font-normal h-7"
83
- >
84
- Portfolio
85
- </Badge>
86
- {/* Heading */}
87
- <div className="max-w-xs sm:max-w-2xl mx-auto text-center">
88
- <h2 className="text-foreground text-3xl sm:text-5xl font-semibold">
89
- How we transformed a small business’s{" "}
90
- <span className="font-serif italic font-normal">online presence</span>
91
- </h2>
92
- </div>
93
- </div>
94
- {/* portfolio */}
95
- <div className="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-x-7 md:gap-y-8 w-full">
96
- {portfolioData.map((item, index) => (
97
- <m.div
98
- key={index}
99
- initial="hidden"
100
- whileInView="visible"
101
- viewport={{ once: true }}
102
- custom={index}
103
- variants={cardVariants}
104
- className="group"
105
- >
106
- <Card className="p-0 ring-0 overflow-hidden shadow-none border-0">
107
- <CardContent className="p-0 flex flex-col gap-6">
108
- <div className="relative aspect-auto overflow-hidden rounded-2xl">
109
- <a href="#">
110
- <img
111
- src={item.portfolio_image}
112
- alt={item.portfolio_title}
113
- width={800}
114
- height={370}
115
- className="rounded-2xl object-cover w-full h-full transition-transform duration-500 group-hover:scale-105"
116
- />
117
- </a>
118
- </div>
119
- <div className="flex flex-col gap-3">
120
- <a
121
- href="#"
122
- className="text-foreground text-2xl font-medium tracking-tighter w-fit"
123
- >
124
- {item.portfolio_title}
125
- </a>
126
- <div className="flex flex-wrap gap-3">
127
- {item.portfolio_tags.map((tag, tagIndex) => (
128
- <Badge
129
- key={tagIndex}
130
- variant="outline"
131
- className="text-sm font-normal px-3 py-1 h-7"
132
- >
133
- {tag}
134
- </Badge>
135
- ))}
136
- </div>
137
- </div>
138
- </CardContent>
139
- </Card>
140
- </m.div>
141
- ))}
142
- </div>
143
- </div>
144
- </div>
145
- </section>
146
- );
147
- };
148
-
149
- export default Portfolio;