diff --git a/apps/web/package.json b/apps/web/package.json
index ebc50cb4..73016f52 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,6 +1,6 @@
{
"name": "1chooo-com",
- "version": "0.5.0",
+ "version": "0.5.1",
"private": true,
"license": "CC-BY-4.0",
"author": {
@@ -29,6 +29,7 @@
"@next/mdx": "^15.0.3",
"@next/third-parties": "^15.0.1",
"@primer/octicons-react": "^19.12.0",
+ "@radix-ui/react-icons": "^1.3.2",
"@repo/math": "*",
"@repo/ui": "*",
"@testing-library/user-event": "^14.5.2",
@@ -38,7 +39,6 @@
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"bootstrap": "^5.3.3",
- "caniuse-lite": "^1.0.30001653",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cobe": "^0.6.3",
diff --git a/apps/web/src/app/gallery/page.tsx b/apps/web/src/app/gallery/page.tsx
new file mode 100644
index 00000000..930e8569
--- /dev/null
+++ b/apps/web/src/app/gallery/page.tsx
@@ -0,0 +1,23 @@
+import type { Metadata } from "next";
+import PageHeader from '@/components/page-header';
+import config from '@/config';
+import { BlurFadeDemo } from "@/components/magicui/blur-fade-demo";
+import { AnimatedGradientTextDemo } from "@/components/magicui/animated-gradient-text-demo";
+
+const { title } = config;
+
+export const metadata: Metadata = {
+ title: `Gallery | ${title}`,
+};
+
+const Gallery = async () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default Gallery;
diff --git a/apps/web/src/app/sitemap.ts b/apps/web/src/app/sitemap.ts
index 86e385b3..98443f33 100644
--- a/apps/web/src/app/sitemap.ts
+++ b/apps/web/src/app/sitemap.ts
@@ -26,7 +26,7 @@ export default async function sitemap() {
'/resume',
'/portfolio',
'/post',
- // '/contact'
+ '/gallery'
].map((route) => ({
url: `https://1chooo.com${route}`,
lastModified: new Date().toISOString().split('T')[0],
diff --git a/apps/web/src/components/about/latest-articles.tsx b/apps/web/src/components/about/latest-articles.tsx
index 56ffabfe..9544d890 100644
--- a/apps/web/src/components/about/latest-articles.tsx
+++ b/apps/web/src/components/about/latest-articles.tsx
@@ -5,9 +5,10 @@ import Link from "next/link";
import Image from "next/image";
import MarkdownRenderer from "@/components/markdown/markdown-renderer";
import { sendGTMEvent } from "@/components/google";
-import SeeMoreButton from "@/components/about/see-more-button";
import { LuEye } from "react-icons/lu";
import { ArrowRightIcon } from "@primer/octicons-react";
+import { cn } from "@/lib/utils";
+import AnimatedShinyText from "@/components/magicui/animated-shiny-text";
import "@/styles/about/latest-posts.css";
@@ -78,7 +79,20 @@ const LatestArticles = ({ posts }: { posts: Post[] }) => {
))}
-
+
+
+
+
+ ✨ See More Posts
+
+
+
+
+
);
};
diff --git a/apps/web/src/components/magicui/animated-button.tsx b/apps/web/src/components/magicui/animated-button.tsx
deleted file mode 100644
index 2273a994..00000000
--- a/apps/web/src/components/magicui/animated-button.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-'use client'
-
-import { motion } from 'framer-motion'
-import Link from 'next/link'
-import type { IconType as ReactIconType } from "react-icons"
-import type { Icon as OcticonsType } from "@primer/octicons-react"
-import { sendGTMEvent } from "@/components/google"
-
-type AnimatedButtonProps = {
- path: string
- bannerText: string
- icon: ReactIconType | OcticonsType
- onClick?: () => void
-}
-
-const AnimatedButton: React.FC = ({ path, bannerText, icon: Icon, onClick }) => {
- const handleClick = (e: React.MouseEvent) => {
- if (onClick) {
- e.preventDefault()
- onClick()
- }
- sendGTMEvent({
- event: 'animatedButtonClick',
- value: 'GTM-PDJ3NF4Q'
- })
- }
-
- return (
-
-
-
- {bannerText}
-
-
-
-
-
-
- )
-}
-
-export default AnimatedButton
diff --git a/apps/web/src/components/magicui/animated-gradient-text-demo.tsx b/apps/web/src/components/magicui/animated-gradient-text-demo.tsx
new file mode 100644
index 00000000..0130f0a1
--- /dev/null
+++ b/apps/web/src/components/magicui/animated-gradient-text-demo.tsx
@@ -0,0 +1,30 @@
+import { ChevronRight } from "lucide-react";
+import Link from "next/link";
+
+import { cn } from "@/lib/utils";
+import AnimatedGradientText from "@/components/magicui/animated-gradient-text";
+
+export async function AnimatedGradientTextDemo() {
+ return (
+
+
+
+ 🎉
{" "}
+
+ Thanks for Magic UI!
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/src/components/magicui/animated-gradient-text.tsx b/apps/web/src/components/magicui/animated-gradient-text.tsx
new file mode 100644
index 00000000..19b0e628
--- /dev/null
+++ b/apps/web/src/components/magicui/animated-gradient-text.tsx
@@ -0,0 +1,26 @@
+import { ReactNode } from "react";
+
+import { cn } from "@/lib/utils";
+
+export default function AnimatedGradientText({
+ children,
+ className,
+}: {
+ children: ReactNode;
+ className?: string;
+}) {
+ return (
+
+ );
+}
diff --git a/apps/web/src/components/magicui/animated-shiny-text.tsx b/apps/web/src/components/magicui/animated-shiny-text.tsx
new file mode 100644
index 00000000..fb0cdc8b
--- /dev/null
+++ b/apps/web/src/components/magicui/animated-shiny-text.tsx
@@ -0,0 +1,43 @@
+import { CSSProperties, FC, ReactNode } from "react";
+
+import { cn } from "@/lib/utils";
+
+interface AnimatedShinyTextProps {
+ children: ReactNode;
+ className?: string;
+ shimmerWidth?: number;
+}
+
+/**
+ * @link https://magicui.design/docs/components/animated-shiny-text
+ */
+const AnimatedShinyText: FC = ({
+ children,
+ className,
+ shimmerWidth = 100,
+}) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default AnimatedShinyText;
diff --git a/apps/web/src/components/magicui/blur-fade-demo.tsx b/apps/web/src/components/magicui/blur-fade-demo.tsx
new file mode 100644
index 00000000..e8542845
--- /dev/null
+++ b/apps/web/src/components/magicui/blur-fade-demo.tsx
@@ -0,0 +1,26 @@
+import BlurFade from "@/components/magicui/blur-fade";
+
+const images = Array.from({ length: 9 }, (_, i) => {
+ const isLandscape = i % 2 === 0;
+ const width = isLandscape ? 800 : 600;
+ const height = isLandscape ? 600 : 800;
+ return `https://picsum.photos/seed/${i + 1}/${width}/${height}`;
+});
+
+export function BlurFadeDemo() {
+ return (
+
+
+ {images.map((imageUrl, idx) => (
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/apps/web/src/components/magicui/blur-fade.tsx b/apps/web/src/components/magicui/blur-fade.tsx
new file mode 100644
index 00000000..2b750921
--- /dev/null
+++ b/apps/web/src/components/magicui/blur-fade.tsx
@@ -0,0 +1,83 @@
+"use client";
+
+import { useRef } from "react";
+import {
+ AnimatePresence,
+ motion,
+ useInView,
+ UseInViewOptions,
+ Variants,
+} from "framer-motion";
+
+type MarginType = UseInViewOptions["margin"];
+
+interface BlurFadeProps {
+ children: React.ReactNode;
+ className?: string;
+ variant?: {
+ hidden: { y: number };
+ visible: { y: number };
+ };
+ duration?: number;
+ delay?: number;
+ yOffset?: number;
+ inView?: boolean;
+ inViewMargin?: MarginType;
+ blur?: string;
+}
+
+/**
+ * @example
+ *
+ */
+
+export default function BlurFade({
+ children,
+ className,
+ variant,
+ duration = 0.4,
+ delay = 0,
+ yOffset = 6,
+ inView = false,
+ inViewMargin = "-50px",
+ blur = "6px",
+}: BlurFadeProps) {
+ const ref = useRef(null);
+ const inViewResult = useInView(ref, { once: true, margin: inViewMargin });
+ const isInView = !inView || inViewResult;
+ const defaultVariants: Variants = {
+ hidden: { y: yOffset, opacity: 0, filter: `blur(${blur})` },
+ visible: { y: -yOffset, opacity: 1, filter: `blur(0px)` },
+ };
+ const combinedVariants = variant || defaultVariants;
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/apps/web/src/config/index.ts b/apps/web/src/config/index.ts
index 8aa071a9..257918de 100644
--- a/apps/web/src/config/index.ts
+++ b/apps/web/src/config/index.ts
@@ -50,6 +50,7 @@ const config: Config = {
{ path: '/resume', label: 'Resume' },
{ path: '/portfolio', label: 'Portfolio' },
{ path: '/post', label: 'Post' },
+ { path: '/gallery', label: 'Gallery' },
],
socialLinks: [
{ url: `https://github.com/1chooo`, icon: LuGithub, name: 'GitHub' },
diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js
index ecdd1454..478e587c 100644
--- a/apps/web/tailwind.config.js
+++ b/apps/web/tailwind.config.js
@@ -33,6 +33,35 @@ module.exports = {
md: `calc(var(--radius) - 2px)`,
sm: "calc(var(--radius) - 4px)",
},
+ animation: {
+ "shiny-text": "shiny-text 8s infinite",
+ gradient: "gradient 8s linear infinite",
+ marquee: "marquee var(--duration) linear infinite",
+ "marquee-vertical": "marquee-vertical var(--duration) linear infinite",
+ },
+ keyframes: {
+ "shiny-text": {
+ "0%, 90%, 100%": {
+ "background-position": "calc(-100% - var(--shiny-width)) 0",
+ },
+ "30%, 60%": {
+ "background-position": "calc(100% + var(--shiny-width)) 0",
+ },
+ },
+ gradient: {
+ to: {
+ backgroundPosition: "var(--bg-size) 0",
+ },
+ },
+ marquee: {
+ from: { transform: "translateX(0)" },
+ to: { transform: "translateX(calc(-100% - var(--gap)))" },
+ },
+ "marquee-vertical": {
+ from: { transform: "translateY(0)" },
+ to: { transform: "translateY(calc(-100% - var(--gap)))" },
+ },
+ },
},
},
plugins: [require("tailwindcss-animate")],