From 3bc4ac1375641b875b7378b3ab8771cc57aad4c1 Mon Sep 17 00:00:00 2001 From: Prasheel Soni Date: Sat, 14 Feb 2026 10:12:44 +0200 Subject: [PATCH 1/2] style(portfolio): revamp ui ux and move to neobrutalism --- components.json | 21 + components/card/card.tsx | 144 +-- components/header/header.tsx | 150 +-- components/instagram/gallery.tsx | 111 +-- components/layout/Layout.tsx | 20 +- components/profile/profile.tsx | 5 + components/smooth-scroll.tsx | 59 ++ components/tailwind/badge.tsx | 8 - components/tailwind/button.tsx | 21 - components/tailwind/section.tsx | 33 +- components/ui/accordion.tsx | 56 ++ components/ui/avatar.tsx | 49 + components/ui/badge.tsx | 39 + components/ui/button.tsx | 54 + components/ui/card.tsx | 65 ++ components/ui/carousel.tsx | 240 +++++ components/ui/sheet.tsx | 125 +++ lib/utils.ts | 6 + next-env.d.ts | 2 +- package-lock.json | 1475 +++++++++++++++------------- package.json | 17 +- pages/_app.tsx | 19 +- pages/_document.tsx | 6 +- pages/index.tsx | 10 +- postcss.config.js | 3 +- sections/about/about.tsx | 630 +++++------- sections/banner/banner.tsx | 246 +++-- sections/blog/blog.tsx | 144 ++- sections/experience/experience.tsx | 83 ++ sections/footer/footer.tsx | 87 +- sections/interests/interests.tsx | 227 +++-- sections/map/map.tsx | 106 +- styles/global.scss | 113 ++- styles/theme.ts | 14 - tailwind.config.js | 54 +- tsconfig.json | 22 +- 36 files changed, 2719 insertions(+), 1745 deletions(-) create mode 100644 components.json create mode 100644 components/smooth-scroll.tsx delete mode 100644 components/tailwind/badge.tsx delete mode 100644 components/tailwind/button.tsx create mode 100644 components/ui/accordion.tsx create mode 100644 components/ui/avatar.tsx create mode 100644 components/ui/badge.tsx create mode 100644 components/ui/button.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/carousel.tsx create mode 100644 components/ui/sheet.tsx create mode 100644 lib/utils.ts create mode 100644 sections/experience/experience.tsx delete mode 100644 styles/theme.ts diff --git a/components.json b/components.json new file mode 100644 index 0000000..3212131 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "styles/global.scss", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/components/card/card.tsx b/components/card/card.tsx index 02e007e..b314e8a 100644 --- a/components/card/card.tsx +++ b/components/card/card.tsx @@ -1,76 +1,92 @@ +"use client"; + import Link from "next/link"; import Image from "next/image"; -import { Text, Title, Box } from "@mantine/core"; -import Badge from "../tailwind/badge"; -import Button from "../tailwind/button"; +import { motion } from "framer-motion"; +import { + Card as UICard, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; interface CardProps { - thumbnail: string; - title: string; - shortDescription: string; - tags: string; - link: string; + thumbnail: string; + title: string; + shortDescription: string; + tags: string; + link: string; } const Card = ({ - thumbnail, title, shortDescription, tags, link, - }: CardProps) => { - let tagsArray: string[] = []; - if (tags) { - tagsArray = Array.isArray(tags) ? tags : tags.split(",").map(tag => tag.trim()); - } - - const getLink = (link: string) => { - if (link.startsWith("http")) { - return link; - } else { - return `/blog/${link}`; - } - }; + thumbnail, + title, + shortDescription, + tags, + link, +}: CardProps) => { + const tagsArray: string[] = tags + ? Array.isArray(tags) + ? (tags as unknown as string[]) + : (tags as string).split(",").map((tag) => (tag as string).trim()) + : []; - const heightWithThumbnail = "430px"; - const heightWithoutThumbnail = "280px"; + const getLink = (href: string) => + href.startsWith("http") ? href : `/blog/${href}`; - return ( - - - {thumbnail && ( -
- {`Blog -
- )} - -
- {title} - {shortDescription} -
- { - tagsArray.length ? - tagsArray.map( - (tag) => , - ) - : ""} -
-
- -
-
-
- ); + return ( + + + {thumbnail ? ( +
+ {`${title} +
+ ) : null} + + + {title} + + + +

+ {shortDescription} +

+ {tagsArray.length > 0 && ( +
+ {tagsArray.map((tag) => ( + + {tag} + + ))} +
+ )} +
+ + + +
+
+ ); }; export default Card; diff --git a/components/header/header.tsx b/components/header/header.tsx index ee620ce..85f816b 100644 --- a/components/header/header.tsx +++ b/components/header/header.tsx @@ -1,78 +1,92 @@ +"use client"; + import Link from "next/link"; import Image from "next/image"; -import React, { useState } from "react"; -import { Burger, Box, Paper } from "@mantine/core"; -import Button from "../tailwind/button"; +import React from "react"; +import { Menu } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, + SheetTrigger, +} from "@/components/ui/sheet"; export interface HeaderProps { - logoUrl: string; - navMap?: Array<{ href: string, label: string }>; + logoUrl: string; + navMap?: Array<{ href: string; label: string }>; } -export const Header: React.FC = ({logoUrl, navMap = []}) => { - const [opened, setOpened] = useState(false); - const title = opened ? "Close navigation" : "Open navigation"; - - const getHref = (href: string) => { - if (href.startsWith("/")) { - return href; - } - return `/${href}`; - }; +export const Header: React.FC = ({ logoUrl, navMap = [] }) => { + const getHref = (href: string) => { + if (href.startsWith("/")) return href; + return `/${href}`; + }; - return ( - - - - Logo - + const navLinks = ( + <> + {navMap.length > 0 && + navMap.map((item) => ( + + ))} + + ); - - {navMap.length > 0 && navMap.map((item) => ( - - ))} - + return ( +
+
+ + Logo + - - setOpened((o) => !o)} - title={title} - color="white" - /> - - + - {opened && ( - - - - )} - - ); -}; \ No newline at end of file +
+ + + + + + + Menu + +
    + {navMap.length > 0 && + navMap.map((item) => ( +
  • + +
  • + ))} +
+
+
+
+
+
+ ); +}; diff --git a/components/instagram/gallery.tsx b/components/instagram/gallery.tsx index fc9ae84..a6030eb 100644 --- a/components/instagram/gallery.tsx +++ b/components/instagram/gallery.tsx @@ -1,6 +1,9 @@ import React, { useState, useEffect, useCallback } from "react"; import Image from "next/image"; import type { GalleryImage } from "../../interfaces/photo-gallery"; +import { Card, CardContent } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Badge } from "../ui/badge"; interface PhotoGalleryProps { galleryItems: GalleryImage[]; @@ -94,27 +97,26 @@ export default function PhotoGallery({ galleryItems }: PhotoGalleryProps) { return (
-
-

My Photo Book

-

+

+

+ My Photo Book +

+

A collection of moments and memories captured during my journeys around the world.

{/* Filter Buttons */} -
- {dynamicCategories.map(filter => ( - + ))}
@@ -122,33 +124,32 @@ export default function PhotoGallery({ galleryItems }: PhotoGalleryProps) {
{filteredImages.length > 0 ? ( filteredImages.map((image, index) => ( -
openModal(index)} > - {/* Loading spinner */} - {imageLoading[image.id] !== false && ( -
-
-
- )} - {image.caption} handleImageLoad(image.id)} - style={imageLoading[image.id] !== false ? { visibility: "hidden" } : {}} - /> -
-

- {image.caption} -

-

{image.category}

+
+ {imageLoading[image.id] !== false && ( +
+
+
+ )} + {image.caption} handleImageLoad(image.id)} + style={imageLoading[image.id] !== false ? { visibility: "hidden" } : {}} + />
-
+ +

{image.caption}

+ {image.category} +
+ )) ) : (

@@ -161,32 +162,32 @@ export default function PhotoGallery({ galleryItems }: PhotoGalleryProps) { {/* Modal */} {isModalOpen && (

e.stopPropagation()} > -
-

+
+

{modalImageDetails.caption || "Image Preview"}

-
+
{modalImageLoading && ( -
-
+
+
)} {modalImageDetails.caption} setModalImageLoading(false)} style={modalImageLoading ? { visibility: "hidden" } : {}} /> -

{modalImageDetails.caption}

+

{modalImageDetails.caption}

{filteredImages[currentImageIndex]?.location && ( -

{filteredImages[currentImageIndex].location}

+

{filteredImages[currentImageIndex].location}

)}
-
- - + +
diff --git a/components/layout/Layout.tsx b/components/layout/Layout.tsx index 163fa29..d1bcf54 100644 --- a/components/layout/Layout.tsx +++ b/components/layout/Layout.tsx @@ -2,35 +2,31 @@ import React from "react"; import { Header } from "../header/header"; import Footer from "../../sections/footer/footer"; import Meta from "../meta/meta"; +import { SmoothScroll } from "../smooth-scroll"; interface LayoutProps { children: React.ReactNode; data: { - meta?: any; // Make meta optional on data if it can sometimes be missing - header?: any; // Make header optional on data if it can sometimes be missing + meta?: any; + header?: any; }; - about: { // Expecting an object that contains a profiles property - profiles?: any[]; // Make profiles optional on about if it can sometimes be missing + about: { + profiles?: any[]; }; } -const Layout: React.FC = ({ - children, - data, - about, -}) => { - // Destructure with safety for potentially undefined props +const Layout: React.FC = ({ children, data, about }) => { const meta = data?.meta; const header = data?.header; const profiles = about?.profiles; return ( - <> + {meta && } {header &&
}
{children}
{profiles &&