Skip to content

Commit 348303e

Browse files
committed
feat(ui): redesign mobile layout with new interface
1 parent ff8cdf4 commit 348303e

File tree

6 files changed

+98
-40
lines changed

6 files changed

+98
-40
lines changed

app/(root)/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ export default function Portfolio() {
6565

6666
return (
6767
<div className="min-h-screen w-full relative">
68-
<Header />
68+
<Header setActiveTab={setActiveTab} />
6969
<Navigation activeTab={activeTab} setActiveTab={setActiveTab} />
70-
<main className="container mx-auto px-4 lg:px-20 xl:px-32 py-8">{renderActiveTab()}</main>
70+
<main className="container mx-auto px-3 lg:px-20 xl:px-32 py-8">{renderActiveTab()}</main>
7171
<Footer />
7272
</div>
7373
)

app/_components/header.tsx

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ import { cn } from "@/lib/utils"
99
import HeaderSkeleton from './skeleton/header'
1010
import Image from 'next/image'
1111
import Link from 'next/link'
12-
13-
export default function Header() {
12+
import { TABS } from '../(root)/page'
13+
interface HeaderProps {
14+
activeTab?: typeof TABS[number]
15+
setActiveTab: (tab: typeof TABS[number]) => void
16+
}
17+
export default function Header({ setActiveTab }: HeaderProps) {
1418
const { resolvedTheme } = useTheme()
1519
const [isMounted, setIsMounted] = useState(false)
1620

@@ -29,14 +33,14 @@ export default function Header() {
2933
return (
3034
<header className="sticky top-0 z-40 bg-background/80 backdrop-blur-md border-b border-border">
3135
<div className="container mx-auto px-4 py-4 lg:px-20 xl:px-32">
32-
<div className="flex items-center justify-between gap-1">
33-
<div className="flex items-center space-x-4">
36+
<div className="flex items-center justify-between gap-1 pt-6 sm:pt-0">
37+
<div className="flex sm:items-center space-x-4 sm:flex-row gap-4 sm:gap-0 flex-col">
3438
<div className="relative">
3539

3640
{/* use image instead */}
3741
<div
3842
className={cn(
39-
"size-10 sm:size-13 rounded-lg bg-gradient-to-br flex items-center justify-center relative outline outline-offset-[3px] outline-border",
43+
"size-20 sm:size-13 rounded-lg bg-gradient-to-br flex items-center justify-center relative outline outline-offset-[3px] outline-border",
4044
resolvedTheme === "dark"
4145
? "from-zinc-600 to-zinc-900"
4246
: "from-zinc-50 to-zinc-200"
@@ -66,16 +70,40 @@ export default function Header() {
6670
</div> */}
6771
</div>
6872
<div>
69-
<h1 className="text-base sm:text-xl font-medium flex items-center gap-2">
73+
<h1 className="text-xl font-medium flex items-center gap-2">
7074
Alex Developer
7175
<div className="size-1.5 sm:size-2.5 animate-pulse relative after:content-[''] after:absolute flex items-center justify-center after:h-full after:w-full after:bg-green-400 after:rounded-full after:animate-ping rounded-full bg-primary"></div>
7276
</h1>
73-
<p className="text-muted-foreground text-[11px] sm:text-sm">
77+
<p className="text-muted-foreground text-sm">
7478
Full Stack Developer & UI/UX Designer
7579
</p>
7680
</div>
81+
82+
<div className="sm:hidden flex justify-between gap-2 items-center">
83+
{/* Download resume button one more button */}
84+
<a
85+
href="/resume.pdf"
86+
target="_blank"
87+
rel="noopener noreferrer"
88+
aria-label="Open Resume"
89+
>
90+
<Button
91+
variant="outline"
92+
size="sm"
93+
className="rounded-full text-primary hover:bg-primary/70"
94+
>
95+
Download Resume
96+
</Button>
97+
</a>
98+
<Button variant="outline" size="sm" className='rounded-full text-muted-foreground'
99+
onClick={() => setActiveTab("contact")}
100+
aria-label="Contact Me"
101+
>
102+
Contact Me
103+
</Button>
104+
</div>
77105
</div>
78-
<div className="flex items-center space-x-2">
106+
<div className="flex items-center space-x-2 self-start flex-col sm:flex-row sm:self-center gap-1">
79107
<ThemeToggle />
80108
<Link href="https://github.com/psparwez" aria-label="View GitHub Profile" target="_blank" rel="noopener noreferrer">
81109
<Button variant="outline" size="sm">

app/_components/navigation.tsx

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import React from "react"
1+
import React, { Fragment } from "react"
22
import { TABS } from "../(root)/page"
33
import { cn } from "@/lib/utils"
4-
4+
import { useIsMobile } from "@/hooks/use-mobile"
5+
import { User, Briefcase, Folder, Mail } from "lucide-react"
56
interface NavigationProps {
67
activeTab?: typeof TABS[number]
78
setActiveTab: (tab: typeof TABS[number]) => void
@@ -11,31 +12,54 @@ export default function Navigation({
1112
activeTab = "profile",
1213
setActiveTab,
1314
}: NavigationProps) {
15+
1416
const tabs = [
15-
{ id: "profile", label: "Profile" },
16-
{ id: "experience", label: "Experience" },
17-
{ id: "projects", label: "Projects" },
18-
{ id: "contact", label: "Contact" },
17+
{ id: "profile", label: "Profile", icon: <User size={16} /> },
18+
{ id: "experience", label: "Experience", icon: <Briefcase size={16} /> },
19+
{ id: "projects", label: "Projects", icon: <Folder size={16} /> },
20+
{ id: "contact", label: "Contact", icon: <Mail size={16} /> },
1921
] as const
2022

23+
const isMobile = useIsMobile()
24+
2125
return (
22-
<nav className="bg-card relative border-b border-border">
26+
<nav className={`bg-card border-b border-border ${isMobile ? "fixed bottom-0 rounded-ss-2xl left-0 right-0 w-full border-t border-border shadow-lg" : "relative"}`}>
2327
<div className="container mx-auto px-4 lg:px-20 xl:px-32">
24-
<div className="grid grid-cols-2 sm:flex space-x-2 sm:space-x-8 overflow-x-auto">
28+
<div className={`sm:flex space-x-2 sm:space-x-8 overflow-x-auto ${isMobile ? "flex justify-between" : ""}`}>
2529
{tabs.map((tab) => (
26-
<button
27-
key={tab.id}
28-
onClick={() => setActiveTab(tab.id)}
29-
className={cn(
30-
"py-4 px-2 text-sm font-medium border-b-2 transition-colors",
31-
activeTab === tab.id
32-
? "border-primary text-primary"
33-
: "border-transparent text-muted-foreground hover:text-foreground"
34-
)}
35-
aria-label={`Navigate to ${tab.label} section`}
36-
>
37-
{tab.label}
38-
</button>
30+
<Fragment key={tab.id}>
31+
{
32+
isMobile ? (
33+
<button
34+
onClick={() => setActiveTab(tab.id)}
35+
className={cn(
36+
"py-4 px-2 text-[10px] font-medium border-b-2 transition-colors flex flex-col gap-1 items-center justify-between w-full m-0",
37+
activeTab === tab.id
38+
? "border-primary text-primary"
39+
: "border-transparent text-muted-foreground hover:text-foreground"
40+
)}
41+
aria-label={`Navigate to ${tab.label} section`}
42+
>
43+
<span>{tab.icon}</span>
44+
<span>{tab.label}</span>
45+
</button>
46+
)
47+
: (
48+
<button
49+
onClick={() => setActiveTab(tab.id)}
50+
className={cn(
51+
"py-4 px-2 text-sm font-medium border-b-2 transition-colors",
52+
activeTab === tab.id
53+
? "border-primary text-primary"
54+
: "border-transparent text-muted-foreground hover:text-foreground"
55+
)}
56+
aria-label={`Navigate to ${tab.label} section`}
57+
>
58+
{tab.label}
59+
</button>
60+
)
61+
}
62+
</Fragment>
3963
))}
4064
</div>
4165
</div>

app/_components/skeleton/header.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,24 @@ export default function HeaderSkeleton() {
55
<>
66
<header className="sticky top-0 z-40 bg-background/80 backdrop-blur-md border-b border-border">
77
<div className="container mx-auto px-4 py-4 lg:px-20 xl:px-32">
8-
<div className="flex items-center justify-between gap-1">
9-
<div className="flex items-center space-x-4">
10-
<div className="animate-pulse rounded-lg bg-muted size-10 sm:size-13" />
8+
<div className="flex items-center justify-between gap-1 pt-6 sm:pt-0">
9+
<div className="flex sm:items-center space-x-4 sm:flex-row gap-4 sm:gap-0 flex-col">
10+
<div className="animate-pulse rounded-lg bg-muted size-20 sm:size-13" />
1111
<div>
1212
<div className="h-4 w-32 bg-muted rounded mb-1 animate-pulse" />
1313
<div className="h-3 w-48 bg-muted rounded animate-pulse" />
1414
</div>
1515
</div>
16-
<div className="flex items-center space-x-2">
16+
<div className="flex items-center gap-1 self-start flex-col sm:flex-row sm:self-center">
1717
<div className="h-9 w-9 rounded-md bg-muted animate-pulse" />
18-
<div className="h-9 w-28 bg-muted rounded-md animate-pulse hidden sm:block" />
18+
<div className="h-9 w-9 sm:w-28 bg-muted rounded-md animate-pulse" />
1919
</div>
20+
21+
</div>
22+
<div className="sm:hidden flex mt-5 gap-2 items-center w-full">
23+
{/* Download resume button one more button */}
24+
<div className="w-40 h-8 rounded-full bg-muted animate-pulse "></div>
25+
<div className="w-28 h-8 rounded-full bg-muted animate-pulse "></div>
2026
</div>
2127
</div>
2228
</header>

components/ui/card.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const CardHeader = React.forwardRef<
2323
>(({ className, ...props }, ref) => (
2424
<div
2525
ref={ref}
26-
className={cn("flex flex-col space-y-1.5 p-6", className)}
26+
className={cn("flex flex-col space-y-1.5 p-4 sm:p-6", className)}
2727
{...props}
2828
/>
2929
))
@@ -60,7 +60,7 @@ const CardContent = React.forwardRef<
6060
HTMLDivElement,
6161
React.HTMLAttributes<HTMLDivElement>
6262
>(({ className, ...props }, ref) => (
63-
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
63+
<div ref={ref} className={cn("p-4 sm:p-6 pt-0", className)} {...props} />
6464
))
6565
CardContent.displayName = "CardContent"
6666

@@ -70,7 +70,7 @@ const CardFooter = React.forwardRef<
7070
>(({ className, ...props }, ref) => (
7171
<div
7272
ref={ref}
73-
className={cn("flex items-center p-6 pt-0", className)}
73+
className={cn("flex items-center p-4 sm:p-6 pt-0", className)}
7474
{...props}
7575
/>
7676
))

hooks/use-mobile.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from "react"
22

3-
const MOBILE_BREAKPOINT = 768
3+
const MOBILE_BREAKPOINT = 640
44

55
export function useIsMobile() {
66
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)

0 commit comments

Comments
 (0)