713 lines
28 KiB
TypeScript
713 lines
28 KiB
TypeScript
import { useEffect, useState } from "react"
|
||
import { ArrowDown } from "lucide-react"
|
||
|
||
import type { Route } from "./+types/home"
|
||
import { homepageJsonLd, site } from "../seo"
|
||
|
||
export function meta({}: Route.MetaArgs) {
|
||
return [
|
||
{ title: site.title },
|
||
{
|
||
name: "description",
|
||
content: site.description,
|
||
},
|
||
{ tagName: "link", rel: "canonical", href: site.url },
|
||
{ name: "robots", content: "index, follow" },
|
||
{ property: "og:type", content: "website" },
|
||
{ property: "og:url", content: site.url },
|
||
{ property: "og:title", content: site.title },
|
||
{ property: "og:description", content: site.description },
|
||
{ property: "og:site_name", content: site.name },
|
||
{ name: "twitter:card", content: "summary_large_image" },
|
||
{ name: "twitter:title", content: site.title },
|
||
{ name: "twitter:description", content: site.description },
|
||
{ "script:ld+json": homepageJsonLd },
|
||
]
|
||
}
|
||
|
||
const navItems = [
|
||
{ label: "skills", id: "skills" },
|
||
{ label: "experience", id: "experience" },
|
||
{ label: "projects", id: "projects" },
|
||
{ label: "campus", id: "campus-experience" },
|
||
{ label: "education", id: "education" },
|
||
{ label: "contact", id: "contact" },
|
||
]
|
||
|
||
const skills = [
|
||
{
|
||
label: "Language",
|
||
items: ["Go", "TypeScript", "JavaScript", "Java", "Python"],
|
||
},
|
||
{
|
||
label: "Framework",
|
||
items: [
|
||
"Next.js",
|
||
"React",
|
||
"React Router",
|
||
"Spring Boot",
|
||
"Tailwind CSS",
|
||
"Quart",
|
||
"Scrapy",
|
||
],
|
||
},
|
||
{
|
||
label: "Database",
|
||
items: ["PostgreSQL", "MySQL", "Redis", "SQLite"],
|
||
},
|
||
{
|
||
label: "Tools",
|
||
items: ["Docker", "Git", "Gitea", "gRPC", "GORM", "Drizzle ORM"],
|
||
},
|
||
];
|
||
|
||
const projects = [
|
||
{
|
||
code: "PRJ-01",
|
||
name: "Tunnl Please",
|
||
description: "SSH tunneling platform.",
|
||
source: "https://git.fossy.my.id/bagas/tunnl_please",
|
||
live: "https://tunel.live/",
|
||
},
|
||
{
|
||
code: "PRJ-02",
|
||
name: "Filekeeper",
|
||
description: "Secure file-hosting platform.",
|
||
source: "https://git.fossy.my.id/bagas/filekeeper",
|
||
},
|
||
{
|
||
code: "PRJ-03",
|
||
name: "Cutit",
|
||
description: "URL shortener.",
|
||
source: "https://github.com/fossyy/cutit",
|
||
},
|
||
]
|
||
|
||
const education = [
|
||
{
|
||
dates: "2025 — Present",
|
||
title: "Bachelor of Information Systems",
|
||
school: "University of Indonesia",
|
||
},
|
||
{
|
||
dates: "2021 — 2024",
|
||
title: "Pengembangan Perangkat Lunak dan Gim — PPLG",
|
||
school: "SMKN 2 GUGUAK",
|
||
},
|
||
]
|
||
|
||
const campusExperience = [
|
||
{
|
||
number: "05.1",
|
||
dates: "March 2026 — Present",
|
||
type: "CAMPUS ORGANIZATION",
|
||
role: "Member of Web Development SIG",
|
||
organization: "RISTEK Fakultas Ilmu Komputer Universitas Indonesia",
|
||
description:
|
||
"Member of the Web Development Special Interest Group at RISTEK Fakultas Ilmu Komputer Universitas Indonesia.",
|
||
},
|
||
{
|
||
number: "05.2",
|
||
dates: "March 2026 — Present",
|
||
type: "EVENT COMMITTEE",
|
||
role: "DevOps Specialist",
|
||
organization: "Open House Fasilkom UI",
|
||
description:
|
||
"Responsible for the DevOps function within the Open House Fasilkom UI event committee.",
|
||
},
|
||
{
|
||
number: "05.3",
|
||
dates: "March 2026 — Present",
|
||
type: "EVENT COMMITTEE",
|
||
role: "Web Developer",
|
||
organization: "COMPFEST",
|
||
description:
|
||
"Contributes as a Web Developer within the COMPFEST event committee.",
|
||
},
|
||
]
|
||
|
||
const externalLinkProps = {
|
||
target: "_blank",
|
||
rel: "noopener noreferrer",
|
||
} as const
|
||
|
||
const terminalCommand = "whoami"
|
||
const fallbackIpAddress = "67.67.67.67"
|
||
const terminalOutput = [
|
||
"fullstack developer",
|
||
"information systems student",
|
||
"building efficient systems",
|
||
]
|
||
|
||
function Cursor({ color = "orange" }: { color?: "orange" | "cyan" }) {
|
||
return (
|
||
<span
|
||
aria-hidden="true"
|
||
className={`terminal-cursor ml-1 inline-block h-4 w-2 align-middle ${
|
||
color === "cyan" ? "bg-cyan-dark" : "bg-orange"
|
||
}`}
|
||
/>
|
||
)
|
||
}
|
||
|
||
function SectionHeading({ number, children }: { number: string; children: string }) {
|
||
return (
|
||
<div className="mb-12 flex items-baseline gap-4">
|
||
<span className="font-terminal text-xs text-orange-dark">{number}</span>
|
||
<h2 className="font-display text-4xl tracking-wide text-ink sm:text-5xl">
|
||
{children}
|
||
</h2>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
function Tag({ children }: { children: string }) {
|
||
return (
|
||
<span className="border border-outline bg-panel px-2 py-1 font-terminal text-xs text-ink">
|
||
{children}
|
||
</span>
|
||
)
|
||
}
|
||
|
||
function HeroTerminal() {
|
||
const [ipAddress, setIpAddress] = useState(fallbackIpAddress)
|
||
const [typedSshCommand, setTypedSshCommand] = useState("")
|
||
const [isConnected, setIsConnected] = useState(false)
|
||
const [typedCommand, setTypedCommand] = useState("")
|
||
const [visibleOutput, setVisibleOutput] = useState(0)
|
||
|
||
useEffect(() => {
|
||
const controller = new AbortController()
|
||
let cancelled = false
|
||
const timers: Array<ReturnType<typeof setTimeout>> = []
|
||
const schedule = (callback: () => void, delay: number) => {
|
||
timers.push(setTimeout(callback, delay))
|
||
}
|
||
const requestTimeout = setTimeout(() => controller.abort(), 3000)
|
||
|
||
const runSequence = (visitorIp: string) => {
|
||
const sshCommand = `ssh portofolio@${visitorIp}`
|
||
|
||
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
||
setTypedSshCommand(sshCommand)
|
||
setIsConnected(true)
|
||
setTypedCommand(terminalCommand)
|
||
setVisibleOutput(terminalOutput.length)
|
||
return
|
||
}
|
||
|
||
const initialDelay = 400
|
||
const sshTypingSpeed = 55
|
||
|
||
for (let index = 1; index <= sshCommand.length; index += 1) {
|
||
schedule(
|
||
() => setTypedSshCommand(sshCommand.slice(0, index)),
|
||
initialDelay + index * sshTypingSpeed,
|
||
)
|
||
}
|
||
|
||
const connectedAt = initialDelay + sshCommand.length * sshTypingSpeed + 450
|
||
schedule(() => setIsConnected(true), connectedAt)
|
||
|
||
const whoamiStart = connectedAt + 550
|
||
for (let index = 1; index <= terminalCommand.length; index += 1) {
|
||
schedule(
|
||
() => setTypedCommand(terminalCommand.slice(0, index)),
|
||
whoamiStart + index * 90,
|
||
)
|
||
}
|
||
|
||
const outputStart = whoamiStart + terminalCommand.length * 90 + 450
|
||
terminalOutput.forEach((_, index) => {
|
||
schedule(() => setVisibleOutput(index + 1), outputStart + index * 260)
|
||
})
|
||
}
|
||
|
||
const loadIpAndRun = async () => {
|
||
let visitorIp = fallbackIpAddress
|
||
|
||
try {
|
||
const response = await fetch("https://ipinfo.io/json", {
|
||
signal: controller.signal,
|
||
})
|
||
if (!response.ok) throw new Error("Unable to resolve visitor IP")
|
||
|
||
const result: unknown = await response.json()
|
||
if (
|
||
typeof result === "object" &&
|
||
result !== null &&
|
||
"ip" in result &&
|
||
typeof result.ip === "string" &&
|
||
result.ip.trim()
|
||
) {
|
||
visitorIp = result.ip.trim()
|
||
}
|
||
} catch {
|
||
visitorIp = fallbackIpAddress
|
||
} finally {
|
||
clearTimeout(requestTimeout)
|
||
}
|
||
|
||
if (!cancelled) {
|
||
setIpAddress(visitorIp)
|
||
runSequence(visitorIp)
|
||
}
|
||
}
|
||
|
||
void loadIpAndRun()
|
||
|
||
return () => {
|
||
cancelled = true
|
||
controller.abort()
|
||
clearTimeout(requestTimeout)
|
||
timers.forEach(clearTimeout)
|
||
}
|
||
}, [])
|
||
|
||
const sshCommand = `ssh portofolio@${ipAddress}`
|
||
const isTypingSsh = typedSshCommand.length < sshCommand.length
|
||
const isTyping = typedCommand.length < terminalCommand.length
|
||
const isComplete = visibleOutput === terminalOutput.length
|
||
|
||
return (
|
||
<div
|
||
aria-label={`Terminal commands: ssh portofolio at ${ipAddress}, then whoami. Output: ${terminalOutput.join(", ")}.`}
|
||
className="flex min-h-48 flex-col items-start justify-center bg-paper p-6 font-terminal text-xs text-cyan-dark"
|
||
>
|
||
<div aria-hidden="true">
|
||
<span className="text-muted">browser:~$</span>{" "}
|
||
{typedSshCommand}
|
||
{isTypingSsh && <Cursor color="cyan" />}
|
||
</div>
|
||
<div aria-hidden="true" className="min-h-[4.5rem]">
|
||
{isConnected && (
|
||
<>
|
||
<div>connected to portofolio@{ipAddress}</div>
|
||
<div>
|
||
<span className="text-muted">portofolio@portfolio:~$</span>{" "}
|
||
{typedCommand}
|
||
{isTyping && <Cursor color="cyan" />}
|
||
</div>
|
||
{terminalOutput.slice(0, visibleOutput).map((line) => (
|
||
<div key={line}>{line}</div>
|
||
))}
|
||
</>
|
||
)}
|
||
{isConnected && !isTyping && !isComplete && <Cursor color="cyan" />}
|
||
{isConnected && isComplete && (
|
||
<div>
|
||
<Cursor color="cyan" />
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default function Home() {
|
||
return (
|
||
<div className="min-h-screen overflow-x-hidden bg-paper text-ink antialiased">
|
||
<div
|
||
aria-hidden="true"
|
||
className="scanlines pointer-events-none fixed inset-0 z-[60]"
|
||
/>
|
||
|
||
<header className="fixed inset-x-0 top-0 z-50 border-b border-outline bg-paper">
|
||
<div className="flex h-12 items-stretch">
|
||
<a
|
||
href="#home"
|
||
className="flex items-center border-r border-outline bg-panel px-4 font-terminal text-sm font-bold tracking-wider text-orange-dark focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-orange-dark"
|
||
>
|
||
BAGAS
|
||
</a>
|
||
<nav aria-label="Primary" className="hidden flex-1 items-stretch md:flex">
|
||
{navItems.map((item) => (
|
||
<a
|
||
key={item.id}
|
||
href={`#${item.id}`}
|
||
className="flex items-center border-r border-outline px-4 font-terminal text-sm font-bold tracking-wide text-muted transition-colors hover:bg-panel-high hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-orange-dark"
|
||
>
|
||
{item.label}
|
||
</a>
|
||
))}
|
||
</nav>
|
||
<div className="ml-auto flex shrink-0 items-center px-4 font-terminal text-xs text-muted">
|
||
<span
|
||
aria-hidden="true"
|
||
className="mr-2 block h-2 w-2 border border-cyan-dark bg-cyan"
|
||
/>
|
||
available for collaboration
|
||
</div>
|
||
</div>
|
||
<nav
|
||
aria-label="Mobile primary"
|
||
className="flex h-10 items-stretch overflow-x-auto border-t border-outline md:hidden"
|
||
>
|
||
{navItems.map((item) => (
|
||
<a
|
||
key={item.id}
|
||
href={`#${item.id}`}
|
||
className="flex shrink-0 items-center border-r border-outline px-4 font-terminal text-xs font-bold tracking-wide text-muted transition-colors hover:bg-panel-high hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-orange-dark"
|
||
>
|
||
{item.label}
|
||
</a>
|
||
))}
|
||
</nav>
|
||
</header>
|
||
|
||
<main
|
||
id="home"
|
||
className="relative z-20 mx-auto w-full max-w-[720px] px-5 pt-[88px] md:px-0 md:pt-24"
|
||
>
|
||
<section className="relative flex min-h-[870px] flex-col justify-center border-x border-outline">
|
||
<div
|
||
aria-hidden="true"
|
||
className="pointer-events-none absolute inset-0 -z-10 flex select-none items-center justify-center overflow-hidden opacity-30"
|
||
>
|
||
<span className="-ml-20 whitespace-nowrap font-display text-[120px] leading-[0.92] tracking-[-0.02em] text-panel-high md:text-[300px] md:tracking-[-0.04em]">
|
||
bagas
|
||
</span>
|
||
</div>
|
||
|
||
<h1 className="pt-12 text-center font-terminal text-xs text-muted">
|
||
developer / student / self-hoster
|
||
</h1>
|
||
|
||
<div className="flex justify-center border-b border-outline p-8 sm:p-12">
|
||
<div className="w-full max-w-lg border border-outline bg-panel">
|
||
<div className="flex items-center border-b border-outline bg-panel-high p-2">
|
||
<div aria-hidden="true" className="flex gap-1">
|
||
<span className="h-3 w-3 border border-outline" />
|
||
<span className="h-3 w-3 border border-outline" />
|
||
<span className="h-3 w-3 border border-outline" />
|
||
</div>
|
||
<span className="ml-2 font-terminal text-xs text-muted">terminal</span>
|
||
</div>
|
||
<HeroTerminal />
|
||
</div>
|
||
</div>
|
||
|
||
<div className="border-b border-outline p-6">
|
||
<div className="mx-auto max-w-2xl space-y-4 font-body text-lg leading-7">
|
||
<p>
|
||
I’m a Fullstack Developer and Information Systems student at the University of Indonesia with experience in backend development, infrastructure, automation, and self-hosted systems. I enjoy building efficient, secure, and reliable solutions while continuously learning new technologies.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex flex-col items-center justify-center gap-4 p-12 sm:flex-row">
|
||
<a
|
||
href="#projects"
|
||
className="border border-outline bg-panel p-2 font-terminal text-xs transition-colors hover:bg-panel-high focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark"
|
||
>
|
||
<span className="mr-1 text-muted">$</span>view projects
|
||
</a>
|
||
<a
|
||
href="#contact"
|
||
className="border border-outline bg-panel p-2 font-terminal text-xs transition-colors hover:bg-panel-high focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark"
|
||
>
|
||
<span className="mr-1 text-muted">$</span>get in touch
|
||
<Cursor />
|
||
</a>
|
||
</div>
|
||
|
||
<div className="flex items-center justify-center gap-2 border-t border-outline p-12 font-terminal text-xs text-muted">
|
||
<span>scroll to inspect</span>
|
||
<ArrowDown aria-hidden="true" size={16} strokeWidth={1.5} />
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="skills"
|
||
className="relative scroll-mt-24 overflow-hidden border-x border-b border-outline px-5 py-24 md:scroll-mt-12 md:px-12"
|
||
>
|
||
<div
|
||
aria-hidden="true"
|
||
className="pointer-events-none absolute inset-0 -z-10 flex select-none items-center justify-center opacity-10"
|
||
>
|
||
<span className="whitespace-nowrap font-display text-[120px] leading-none text-ink md:text-[300px]">
|
||
stack
|
||
</span>
|
||
</div>
|
||
<SectionHeading number="01">technical skills</SectionHeading>
|
||
<div className="space-y-6 border-t border-outline pt-6">
|
||
{skills.map((group) => (
|
||
<div key={group.label}>
|
||
<h3 className="mb-2 font-terminal text-sm font-bold tracking-wide">
|
||
{group.label}
|
||
</h3>
|
||
<div className="flex flex-wrap gap-2">
|
||
{group.items.map((item) => (
|
||
<Tag key={item}>{item}</Tag>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="experience"
|
||
className="scroll-mt-24 border-x border-b border-outline px-5 py-24 md:scroll-mt-12 md:px-12"
|
||
>
|
||
<SectionHeading number="02">experience</SectionHeading>
|
||
<div className="border-t border-outline pt-6">
|
||
<article className="flex flex-col gap-4 md:flex-row">
|
||
<p className="w-48 shrink-0 font-terminal text-xs text-muted">
|
||
Oct 2024 — Apr 2025
|
||
</p>
|
||
<div className="flex-grow">
|
||
<h3 className="font-body text-lg font-bold">Back-end Developer Intern</h3>
|
||
<p className="mb-2 font-terminal text-xs text-cyan-dark">
|
||
PT eBdesk Teknologi
|
||
</p>
|
||
<p className="mb-4 max-w-lg font-body text-[15px] leading-6 text-muted">
|
||
Developed a flexible web-crawling system capable of extracting data
|
||
from different website structures without requiring a completely new
|
||
crawler for every website.
|
||
</p>
|
||
<p className="mb-2 font-terminal text-xs font-bold">
|
||
Dynamic Web Crawling System
|
||
</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
{[
|
||
"Go",
|
||
"Tailwind CSS",
|
||
"Templ",
|
||
"HTMX",
|
||
].map((item) => (
|
||
<Tag key={item}>{item}</Tag>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="projects"
|
||
className="scroll-mt-24 border-x border-b border-outline px-5 py-24 md:scroll-mt-12 md:px-12"
|
||
>
|
||
<SectionHeading number="03">selected projects</SectionHeading>
|
||
<div className="divide-y divide-outline border-t border-outline pt-6">
|
||
{projects.map((project) => (
|
||
<article
|
||
key={project.code}
|
||
className="flex flex-col items-start justify-between gap-4 py-6 first:pt-0 md:flex-row"
|
||
>
|
||
<div>
|
||
<p className="mb-1 font-terminal text-xs text-muted">{project.code}</p>
|
||
<h3 className="font-body text-lg font-bold">{project.name}</h3>
|
||
<p className="mt-2 font-body text-[15px] leading-6 text-muted">
|
||
{project.description}
|
||
</p>
|
||
</div>
|
||
<div className="flex shrink-0 flex-col items-start gap-2 md:items-end">
|
||
<a
|
||
{...externalLinkProps}
|
||
href={project.source}
|
||
aria-label={`Inspect source for ${project.name}`}
|
||
className="font-terminal text-xs text-orange-dark hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark"
|
||
>
|
||
[ inspect_source ]
|
||
</a>
|
||
{project.live && (
|
||
<a
|
||
{...externalLinkProps}
|
||
href={project.live}
|
||
aria-label={`View ${project.name} live`}
|
||
className="font-terminal text-xs text-cyan-dark hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark"
|
||
>
|
||
[ view_live ]
|
||
</a>
|
||
)}
|
||
</div>
|
||
</article>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="campus-experience"
|
||
className="relative scroll-mt-24 overflow-hidden border-x border-b border-outline px-5 py-24 md:scroll-mt-12 md:px-12"
|
||
>
|
||
<SectionHeading number="05">campus technical experience</SectionHeading>
|
||
<div className="border-t border-outline pt-6">
|
||
<p className="mb-12 max-w-2xl font-body text-[15px] leading-6">
|
||
Technical involvement in campus organizations and event committees,
|
||
focusing on web development and infrastructure.
|
||
</p>
|
||
<div className="divide-y divide-outline">
|
||
{campusExperience.map((item) => (
|
||
<article
|
||
key={item.number}
|
||
className="flex flex-col gap-4 py-6 first:pt-0 md:flex-row"
|
||
>
|
||
<div className="flex w-48 shrink-0 flex-col gap-1 font-terminal text-xs">
|
||
<span className="text-orange-dark">{item.number}</span>
|
||
<span className="text-muted">{item.dates}</span>
|
||
<span className="text-muted">{item.type}</span>
|
||
</div>
|
||
<div className="flex-grow">
|
||
<div className="mb-1 flex items-center">
|
||
<span aria-hidden="true" className="mr-2 h-2 w-2 bg-orange" />
|
||
<h3 className="font-body text-lg font-bold">{item.role}</h3>
|
||
</div>
|
||
<p className="mb-2 font-terminal text-xs text-cyan-dark">
|
||
{item.organization}
|
||
</p>
|
||
<p className="max-w-lg font-body text-[15px] leading-6 text-muted">
|
||
{item.description}
|
||
</p>
|
||
<p className="mt-2 font-terminal text-xs text-muted">
|
||
Depok, West Java, Indonesia
|
||
</p>
|
||
</div>
|
||
</article>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="education"
|
||
className="scroll-mt-24 border-x border-b border-outline px-5 py-24 md:scroll-mt-12 md:px-12"
|
||
>
|
||
<SectionHeading number="06">education</SectionHeading>
|
||
<div className="space-y-12 border-t border-outline pt-6">
|
||
{education.map((item) => (
|
||
<article key={item.title} className="flex flex-col gap-4 md:flex-row">
|
||
<p className="w-48 shrink-0 font-terminal text-xs text-muted">{item.dates}</p>
|
||
<div>
|
||
<h3 className="font-body text-lg font-bold">{item.title}</h3>
|
||
<p className="font-terminal text-xs text-cyan-dark">{item.school}</p>
|
||
</div>
|
||
</article>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
<section
|
||
id="contact"
|
||
className="relative scroll-mt-24 overflow-hidden border-x border-outline px-5 py-24 md:scroll-mt-12 md:px-12"
|
||
>
|
||
<div
|
||
aria-hidden="true"
|
||
className="pointer-events-none absolute inset-0 -z-10 flex select-none items-center justify-center opacity-10"
|
||
>
|
||
<span className="whitespace-nowrap font-display text-[120px] leading-none text-ink md:text-[300px]">
|
||
contact
|
||
</span>
|
||
</div>
|
||
<SectionHeading number="07">contact</SectionHeading>
|
||
<div className="border-t border-outline pt-6">
|
||
<p className="mb-12 max-w-2xl font-body text-[15px] leading-6">
|
||
I’m always interested in new opportunities, technical discussions, and
|
||
collaborations. Feel free to reach out.
|
||
</p>
|
||
<div className="grid grid-cols-1 gap-12 md:grid-cols-2">
|
||
<dl className="space-y-2 font-terminal text-xs">
|
||
<div className="flex items-start">
|
||
<dt className="mr-4 w-20 shrink-0 text-muted">email:</dt>
|
||
<dd>
|
||
<a
|
||
href="mailto:bagas@fossy.my.id"
|
||
className="text-orange-dark hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark"
|
||
>
|
||
bagas@fossy.my.id
|
||
</a>
|
||
</dd>
|
||
</div>
|
||
<div className="flex items-start">
|
||
<dt className="mr-4 w-20 shrink-0 text-muted">phone:</dt>
|
||
<dd>
|
||
<a
|
||
href="tel:+6285355003282"
|
||
className="hover:text-orange-dark hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark"
|
||
>
|
||
+62 853-5500-3282
|
||
</a>
|
||
</dd>
|
||
</div>
|
||
<div className="flex items-start">
|
||
<dt className="mr-4 w-20 shrink-0 text-muted">website:</dt>
|
||
<dd>
|
||
<a
|
||
{...externalLinkProps}
|
||
href="https://fossy.my.id/"
|
||
className="text-orange-dark hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark"
|
||
>
|
||
fossy.my.id
|
||
</a>
|
||
</dd>
|
||
</div>
|
||
<div className="flex items-start">
|
||
<dt className="mr-4 w-20 shrink-0 text-muted">github:</dt>
|
||
<dd>
|
||
<a
|
||
{...externalLinkProps}
|
||
href="https://github.com/fossyy"
|
||
className="text-orange-dark hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark"
|
||
>
|
||
github.com/fossyy
|
||
</a>
|
||
</dd>
|
||
</div>
|
||
<div className="flex items-start">
|
||
<dt className="mr-4 w-20 shrink-0 text-muted">linkedin:</dt>
|
||
<dd>
|
||
<a
|
||
{...externalLinkProps}
|
||
href="https://www.linkedin.com/in/bagas-aulia-rezki/"
|
||
className="text-orange-dark hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark"
|
||
>
|
||
in/bagas-aulia-rezki
|
||
</a>
|
||
<Cursor />
|
||
</dd>
|
||
</div>
|
||
</dl>
|
||
<dl className="border border-outline bg-panel p-6 font-terminal text-xs text-muted">
|
||
<div className="mb-2">
|
||
<dt className="inline text-muted">status:</dt>{" "}
|
||
<dd className="inline">open to collaboration</dd>
|
||
</div>
|
||
<div className="mb-2">
|
||
<dt className="inline text-muted">location:</dt>{" "}
|
||
<dd className="inline">depok, indonesia</dd>
|
||
</div>
|
||
<div className="mb-2">
|
||
<dt className="inline text-muted">timezone:</dt>{" "}
|
||
<dd className="inline">utc+7</dd>
|
||
</div>
|
||
<div>
|
||
<dt className="inline text-muted">response:</dt>{" "}
|
||
<dd className="inline">email preferred</dd>
|
||
</div>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<footer className="relative z-20 mx-auto flex w-full max-w-[720px] flex-col items-center justify-between gap-4 border-t border-outline bg-paper px-5 py-12 text-center md:flex-row md:px-6 md:text-left">
|
||
<div className="flex flex-col items-center md:items-start">
|
||
<span className="font-terminal text-sm font-bold tracking-wide text-orange-dark">
|
||
bagas<span aria-hidden="true" className="ml-1 inline-block h-4 w-2 bg-orange" />
|
||
</span>
|
||
<span className="mt-1 font-terminal text-xs text-muted">
|
||
Bagas Aulia Rezki | Fullstack Developer & Information Systems Student
|
||
</span>
|
||
</div>
|
||
<nav aria-label="Footer" className="flex flex-wrap justify-center gap-4 font-terminal text-xs text-muted">
|
||
<a {...externalLinkProps} href="https://github.com/fossyy" className="hover:text-orange-dark focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark">GitHub</a>
|
||
<a {...externalLinkProps} href="https://www.linkedin.com/in/bagas-aulia-rezki/" className="hover:text-orange-dark focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark">LinkedIn</a>
|
||
<a {...externalLinkProps} href="https://fossy.my.id/" className="hover:text-orange-dark focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark">Website</a>
|
||
<a href="#home" className="hover:text-orange-dark focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-dark">Back to top</a>
|
||
</nav>
|
||
<span className="font-terminal text-xs text-muted">© Bagas Aulia Rezki</span>
|
||
</footer>
|
||
</div>
|
||
)
|
||
}
|