Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2e6844781 | ||
|
|
bbcd07213c |
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.next
|
||||
@@ -0,0 +1,36 @@
|
||||
# Personal Website (Next.js)
|
||||
|
||||
Situs personal 5 halaman untuk Assignment 1 Pemrograman Web, dibuat dengan Next.js (App Router) + TypeScript.
|
||||
|
||||
## Menjalankan
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Buka http://localhost:3000
|
||||
|
||||
## Yang perlu kamu ganti
|
||||
|
||||
1. Teks pribadi: `lib/data.ts` (nama, tagline, pendidikan, hobi, kontak, dll).
|
||||
2. Foto: timpa file di `public/images/` dengan fotomu sendiri (nama file sama, format .jpg).
|
||||
Perbarui teks `alt` di `lib/data.ts` agar sesuai isi fotomu.
|
||||
|
||||
## Peta halaman dan metode CSS
|
||||
|
||||
| Route | File | Metode CSS |
|
||||
| ------------ | ----------------------- | -------------------------------------------- |
|
||||
| `/` | `app/page.tsx` | Inline saja |
|
||||
| `/about` | `app/about/page.tsx` | Internal + inline + external + Tailwind |
|
||||
| `/hobbies` | `app/hobbies/page.tsx` | External saja (`public/styles.css`) |
|
||||
| `/gallery` | `app/gallery/page.tsx` | Tailwind (CDN) saja |
|
||||
| `/contact` | `app/contact/page.tsx` | External (alasan ada di komentar halaman) |
|
||||
|
||||
## Catatan penyesuaian ke Next.js
|
||||
|
||||
- Navigasi memakai `<a>` biasa, bukan `next/link`, supaya tiap halaman dimuat penuh seperti situs multipage dan CSS antarhalaman tidak bocor.
|
||||
- Tidak ada CSS global di `layout.tsx`.
|
||||
- Komentar HTML asli dirender lewat komponen `HtmlComment` (terlihat di DevTools > Elements).
|
||||
- Next.js tetap memuat JavaScript bawaannya untuk menjalankan aplikasi. Script tambahan hanya Tailwind CDN.
|
||||
- Tailwind CDN juga dimuat di `/about` karena halaman itu wajib memakai keempat metode CSS.
|
||||
@@ -0,0 +1,118 @@
|
||||
import type { Metadata } from "next";
|
||||
import Script from "next/script";
|
||||
import { NavExternal } from "../../components/nav";
|
||||
import HtmlComment from "../../components/html-comment";
|
||||
import { profile, education, skills, quote } from "../../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Tentang Saya | Personal Website" };
|
||||
|
||||
// css internal: satu blok <style> (di-hoist react ke <head>) dengan selector id dan class
|
||||
const internalCss = `
|
||||
#about-page {
|
||||
min-height: 100vh;
|
||||
background: #eaf3f4;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
}
|
||||
.quote {
|
||||
margin: 0;
|
||||
padding: 16px 20px;
|
||||
border-left: 4px solid #9aa9ad;
|
||||
color: #55666b;
|
||||
font-style: italic;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div id="about-page">
|
||||
{/* external css: file public/styles.css dimuat lewat link, dipakai untuk nav dan wrapper main */}
|
||||
<link rel="stylesheet" href="/styles.css" precedence="default" />
|
||||
{/* internal css: blok style untuk latar halaman dan font (selector #about-page dan .quote) */}
|
||||
<style href="about-internal" precedence="default">{internalCss}</style>
|
||||
{/* tailwind css: script cdn, dipakai untuk komponen badge di bagian biodata */}
|
||||
<Script src="https://cdn.tailwindcss.com" />
|
||||
|
||||
<HtmlComment text="about.html memakai 4 metode css sekaligus, tiap metode diberi label komentar di elemennya" />
|
||||
|
||||
<header className="site-header">
|
||||
<p className="brand">{profile.nickname}</p>
|
||||
<HtmlComment text="external css: kelas site-nav di styles.css mengatur layout navigasi" />
|
||||
<NavExternal current="/about" />
|
||||
</header>
|
||||
|
||||
<HtmlComment text="external css: kelas container di styles.css mengatur lebar dan padding wrapper main" />
|
||||
<main className="container">
|
||||
<section className="panel">
|
||||
<h1>Tentang Saya</h1>
|
||||
<p>
|
||||
Saya {profile.name}, biasa dipanggil {profile.nickname}. Saya tinggal di {profile.city} dan sedang
|
||||
menempuh pendidikan di Program Studi Informatika.
|
||||
</p>
|
||||
<HtmlComment text="tailwind css: kelas utilitas membentuk badge berbentuk pil" />
|
||||
<p>
|
||||
<span className="inline-block rounded-full bg-teal-800 px-3 py-1 text-sm font-semibold text-white">
|
||||
Informatika
|
||||
</span>{" "}
|
||||
<span className="inline-block rounded-full bg-amber-500 px-3 py-1 text-sm font-semibold text-white">
|
||||
Semester 3
|
||||
</span>{" "}
|
||||
<span className="inline-block rounded-full bg-slate-600 px-3 py-1 text-sm font-semibold text-white">
|
||||
{profile.city}
|
||||
</span>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Pendidikan</h2>
|
||||
<HtmlComment text="ol dipakai karena riwayat pendidikan berurutan dari yang paling awal" />
|
||||
<ol>
|
||||
{education.map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Keahlian</h2>
|
||||
<table className="data-table">
|
||||
<caption className="sr-only">Daftar keahlian saya</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Keahlian</th>
|
||||
<th scope="col">Level</th>
|
||||
<th scope="col">Catatan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{skills.map((row) => (
|
||||
<tr key={row.skill}>
|
||||
<td>{row.skill}</td>
|
||||
<td>{row.level}</td>
|
||||
<td>{row.note}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Kutipan favorit</h2>
|
||||
<HtmlComment text="inline css: atribut style pada blockquote menimpa warna dan border dari css internal (.quote)" />
|
||||
<blockquote
|
||||
className="quote"
|
||||
style={{ color: "#0f4c5c", borderLeft: "6px solid #e09f3e", background: "#fff7e6", fontSize: "1.2rem" }}
|
||||
>
|
||||
<p>“{quote.text}”</p>
|
||||
<footer>— {quote.author}</footer>
|
||||
</blockquote>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="site-footer">
|
||||
<p>
|
||||
© {profile.year} {profile.name}
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { Metadata } from "next";
|
||||
import { NavExternal } from "../../components/nav";
|
||||
import HtmlComment from "../../components/html-comment";
|
||||
import { profile, contacts } from "../../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Kontak | Personal Website" };
|
||||
|
||||
export default function ContactPage() {
|
||||
return (
|
||||
<>
|
||||
{/* pilihan css: external. alasannya, halaman ini memakai ulang kelas panel, container, dan nav
|
||||
yang sudah ada di styles.css. inline akan mengulang gaya, tailwind butuh script cdn tambahan
|
||||
padahal halaman ini sederhana dan tidak perlu komponen khusus */}
|
||||
<link rel="stylesheet" href="/styles.css" precedence="default" />
|
||||
<HtmlComment text="contact.html memakai external css karena gayanya sama dengan halaman lain dan cukup satu stylesheet bersama" />
|
||||
|
||||
<header className="site-header">
|
||||
<p className="brand">{profile.nickname}</p>
|
||||
<NavExternal current="/contact" />
|
||||
</header>
|
||||
|
||||
<main className="container">
|
||||
<h1>Kontak</h1>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Hubungi saya</h2>
|
||||
<p>Silakan hubungi saya lewat salah satu kanal berikut. Tidak ada formulir, langsung saja kirim pesan.</p>
|
||||
<ul className="contact-list">
|
||||
{contacts.map((item) => (
|
||||
<li key={item.label}>
|
||||
<span className="contact-label">{item.label}</span>
|
||||
<a
|
||||
href={item.href}
|
||||
{...(item.external ? { target: "_blank", rel: "noopener noreferrer" } : {})}
|
||||
>
|
||||
{item.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="panel">
|
||||
<h2>Lokasi</h2>
|
||||
<p>Saya berdomisili di {profile.city} dan kuliah di Universitas Katolik Widya Mandala Surabaya.</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="site-footer">
|
||||
<p>
|
||||
© {profile.year} {profile.name}
|
||||
</p>
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { Metadata } from "next";
|
||||
import Script from "next/script";
|
||||
import { NavTailwind } from "../../components/nav";
|
||||
import HtmlComment from "../../components/html-comment";
|
||||
import { profile, gallery } from "../../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Galeri | Personal Website" };
|
||||
|
||||
export default function GalleryPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 font-sans text-slate-800">
|
||||
{/* halaman ini WAJIB tailwind css saja: script cdn dimuat di sini, semua gaya berupa kelas utilitas */}
|
||||
<Script src="https://cdn.tailwindcss.com" />
|
||||
|
||||
<header className="mx-auto max-w-5xl px-6 pt-4">
|
||||
<p className="m-0 font-serif text-xl font-bold text-teal-800">{profile.nickname}</p>
|
||||
<NavTailwind current="/gallery" />
|
||||
</header>
|
||||
|
||||
<main className="mx-auto max-w-5xl px-6 pb-6">
|
||||
<section>
|
||||
<h1 className="mb-2 mt-6 font-serif text-4xl font-bold leading-tight text-teal-800">Galeri Foto</h1>
|
||||
<p className="mb-6 max-w-prose leading-relaxed">Beberapa potret dari keseharian saya.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className="mb-4 font-serif text-2xl font-bold text-teal-800">Koleksi terbaru</h2>
|
||||
<HtmlComment text="grid responsif: 1 kolom di ponsel, 2 di tablet, 3 di layar lebar" />
|
||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{gallery.map((photo) => (
|
||||
<figure key={photo.src} className="m-0 overflow-hidden rounded-xl bg-white shadow">
|
||||
<img
|
||||
src={photo.src}
|
||||
alt={photo.alt}
|
||||
width="400"
|
||||
height="300"
|
||||
className="aspect-[4/3] w-full object-cover"
|
||||
/>
|
||||
<figcaption className="px-4 py-3 text-sm font-semibold text-slate-700">{photo.caption}</figcaption>
|
||||
</figure>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="mx-auto max-w-5xl border-t border-slate-300 px-6 pb-8 pt-4 text-sm text-slate-600">
|
||||
<p className="m-0">
|
||||
© {profile.year} {profile.name}
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { Metadata } from "next";
|
||||
import { NavExternal } from "../../components/nav";
|
||||
import HtmlComment from "../../components/html-comment";
|
||||
import { profile, hobbies } from "../../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Hobi | Personal Website" };
|
||||
|
||||
export default function HobbiesPage() {
|
||||
return (
|
||||
<>
|
||||
{/* halaman ini WAJIB external css saja: semua gaya ada di public/styles.css */}
|
||||
<link rel="stylesheet" href="/styles.css" precedence="default" />
|
||||
|
||||
<header className="site-header">
|
||||
<p className="brand">{profile.nickname}</p>
|
||||
<NavExternal current="/hobbies" />
|
||||
</header>
|
||||
|
||||
<main className="container">
|
||||
<h1>Hobi Saya</h1>
|
||||
<p>Tiga hal yang biasa saya lakukan di waktu luang.</p>
|
||||
|
||||
<HtmlComment text="tiap hobi dibuat sebagai section sendiri agar h2 dan h3 tetap berurutan" />
|
||||
{hobbies.map((hobby) => (
|
||||
<section className="hobby" key={hobby.title}>
|
||||
<img src={hobby.photo} alt={hobby.alt} width="260" height="195" />
|
||||
<div className="hobby-text">
|
||||
<h2>{hobby.title}</h2>
|
||||
<p>{hobby.description}</p>
|
||||
<h3>Yang biasa saya lakukan</h3>
|
||||
<ul>
|
||||
{hobby.details.map((detail) => (
|
||||
<li key={detail}>{detail}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
|
||||
<section className="panel">
|
||||
<h2>Ingin mencoba juga?</h2>
|
||||
<p>
|
||||
Untuk pemula yang mau belajar ngegym silahkan{" "}
|
||||
<a href="https://www.tiktok.com/@daddygeeeeee?is_from_webapp=1&sender_device=pc" target="_blank" rel="noopener noreferrer">
|
||||
tonton dia ini
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="site-footer">
|
||||
<p>
|
||||
© {profile.year} {profile.name}
|
||||
</p>
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// root layout sengaja polos: tidak ada css global di sini
|
||||
// setiap halaman membawa metode css-nya sendiri sesuai ketentuan tugas
|
||||
import type { Metadata } from "next";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Personal Website",
|
||||
description: "Situs personal multipage untuk tugas Pemrograman Web",
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html lang="id">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import type { CSSProperties } from "react";
|
||||
import type { Metadata } from "next";
|
||||
import { NavInline } from "../components/nav";
|
||||
import HtmlComment from "../components/html-comment";
|
||||
import { profile } from "../lib/data";
|
||||
|
||||
export const metadata: Metadata = { title: "Beranda | Personal Website" };
|
||||
|
||||
// halaman ini WAJIB inline css saja: semua gaya memakai atribut style
|
||||
const s: Record<string, CSSProperties> = {
|
||||
page: { maxWidth: "960px", margin: "0 auto", padding: "24px", fontFamily: "system-ui, -apple-system, 'Segoe UI', sans-serif", color: "#1c2b33", background: "#f3f7f7", borderRadius: "20px", minHeight: "90vh" },
|
||||
brand: { margin: 0, fontFamily: "Georgia, 'Times New Roman', serif", fontSize: "1.25rem", fontWeight: 700, color: "#0f4c5c" },
|
||||
hero: { display: "flex", flexWrap: "wrap", alignItems: "center", gap: "32px", padding: "32px 0" },
|
||||
photo: { width: "280px", maxWidth: "100%", height: "auto", borderRadius: "16px", border: "6px solid #e09f3e" },
|
||||
heroText: { flex: "1 1 300px" },
|
||||
h1: { margin: "0 0 12px", fontFamily: "Georgia, 'Times New Roman', serif", fontSize: "2.6rem", lineHeight: 1.15, color: "#0f4c5c" },
|
||||
tagline: { margin: "0 0 16px", fontSize: "1.2rem", lineHeight: 1.5 },
|
||||
section: { padding: "24px", background: "#ffffff", borderRadius: "12px", borderLeft: "6px solid #0f4c5c" },
|
||||
h2: { margin: "0 0 8px", fontFamily: "Georgia, 'Times New Roman', serif", fontSize: "1.5rem", color: "#0f4c5c" },
|
||||
p: { margin: 0, lineHeight: 1.7, maxWidth: "65ch" },
|
||||
footer: { marginTop: "32px", paddingTop: "16px", borderTop: "1px solid #c9d8db", fontSize: "0.9rem", color: "#4a5d66" },
|
||||
};
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div style={s.page}>
|
||||
<HtmlComment text="inline css: seluruh halaman ini distyle lewat atribut style sesuai aturan index.html" />
|
||||
<header>
|
||||
<p style={s.brand}>{profile.nickname}</p>
|
||||
<NavInline current="/" />
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section style={s.hero}>
|
||||
<img src={profile.photo} alt={profile.photoAlt} width="280" height="210" style={s.photo} />
|
||||
<div style={s.heroText}>
|
||||
<h1 style={s.h1}>Halo, saya {profile.name}</h1>
|
||||
<p style={s.tagline}>{profile.tagline}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={s.section}>
|
||||
<h2 style={s.h2}>Sekilas tentang saya</h2>
|
||||
<p style={s.p}>{profile.intro}</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer style={s.footer}>
|
||||
<p style={{ margin: 0 }}>
|
||||
© {profile.year} {profile.name}. Dibuat di {profile.city}.
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// next.js tidak punya sintaks komentar html di jsx
|
||||
// komponen ini menulis komentar html asli ke hasil render (lihat di devtools > elements)
|
||||
export default function HtmlComment({ text }: { text: string }) {
|
||||
return <span hidden dangerouslySetInnerHTML={{ __html: `<!-- ${text} -->` }} />;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { CSSProperties } from "react";
|
||||
import { links } from "../lib/data";
|
||||
|
||||
type NavProps = { current: string };
|
||||
|
||||
// catatan: memakai <a> biasa (bukan next/link) agar setiap halaman dimuat penuh
|
||||
// seperti situs multipage html, sehingga css tiap halaman tidak saling bocor
|
||||
|
||||
// nav versi 1: inline css (dipakai index)
|
||||
const inlineNav: Record<string, CSSProperties> = {
|
||||
list: { display: "flex", flexWrap: "wrap", gap: "8px", listStyle: "none", margin: 0, padding: 0 },
|
||||
link: { display: "inline-block", padding: "8px 16px", borderRadius: "999px", textDecoration: "none", fontWeight: 600, color: "#0f4c5c", background: "#e3eef0" },
|
||||
active: { display: "inline-block", padding: "8px 16px", borderRadius: "999px", textDecoration: "none", fontWeight: 600, color: "#ffffff", background: "#0f4c5c" },
|
||||
};
|
||||
|
||||
export function NavInline({ current }: NavProps) {
|
||||
return (
|
||||
<nav aria-label="Navigasi utama" style={{ padding: "16px 0" }}>
|
||||
<ul style={inlineNav.list}>
|
||||
{links.map((item) => (
|
||||
<li key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
style={current === item.href ? inlineNav.active : inlineNav.link}
|
||||
aria-current={current === item.href ? "page" : undefined}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
// nav versi 2: external css (about, hobbies, contact) - kelas ada di public/styles.css
|
||||
export function NavExternal({ current }: NavProps) {
|
||||
return (
|
||||
<nav className="site-nav" aria-label="Navigasi utama">
|
||||
<ul>
|
||||
{links.map((item) => (
|
||||
<li key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
className={current === item.href ? "active" : undefined}
|
||||
aria-current={current === item.href ? "page" : undefined}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
// nav versi 3: tailwind css (gallery)
|
||||
export function NavTailwind({ current }: NavProps) {
|
||||
return (
|
||||
<nav aria-label="Navigasi utama" className="py-4">
|
||||
<ul className="flex flex-wrap gap-2 list-none m-0 p-0">
|
||||
{links.map((item) => (
|
||||
<li key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
aria-current={current === item.href ? "page" : undefined}
|
||||
className={
|
||||
current === item.href
|
||||
? "inline-block rounded-full bg-teal-800 px-4 py-2 font-semibold text-white no-underline"
|
||||
: "inline-block rounded-full bg-teal-50 px-4 py-2 font-semibold text-teal-800 no-underline hover:bg-teal-100"
|
||||
}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
export const profile = {
|
||||
name: "Gray Keiji Matthew Moniaga",
|
||||
nickname: "Gray",
|
||||
tagline: "Hidup Seperti Larry.",
|
||||
intro:
|
||||
"Halo! Saya mahasiswa semester 3 Informatika di Universitas Katolik Widya Mandala Surabaya.",
|
||||
photo: "/images/Profil1.jpeg",
|
||||
photoAlt: "Foto diri saya menghadap kamera",
|
||||
city: "Surabaya",
|
||||
year: 2026,
|
||||
};
|
||||
|
||||
export const links = [
|
||||
{ href: "/", label: "Beranda" },
|
||||
{ href: "/about", label: "Tentang" },
|
||||
{ href: "/hobbies", label: "Hobi" },
|
||||
{ href: "/gallery", label: "Galeri" },
|
||||
{ href: "/contact", label: "Kontak" },
|
||||
];
|
||||
|
||||
export const education = [
|
||||
"SD Tridharma, Manado (2010 - 2016)",
|
||||
"SMP Tridharma, Manado (2016 - 2019)",
|
||||
"SMA Katolik Rex Mundi, Manado (2019 - 2022)",
|
||||
];
|
||||
|
||||
export const skills = [
|
||||
{ skill: "HTML", level: "Pemula", note: "Sedang belajar tag semantik" },
|
||||
{ skill: "CSS", level: "Pemula", note: "Inline, internal, external, Tailwind" },
|
||||
{ skill: "Next.js", level: "Dasar", note: "Dipakai untuk membangun situs ini" },
|
||||
{ skill: "Komunikasi", level: "Menengah", note: "Terbiasa presentasi di kelas" },
|
||||
];
|
||||
|
||||
export const quote = {
|
||||
text: "Satu-satunya cara untuk melakukan pekerjaan hebat adalah mencintai apa yang kamu lakukan.",
|
||||
author: "Steve Jobs",
|
||||
};
|
||||
|
||||
export const hobbies = [
|
||||
{
|
||||
title: "Voli",
|
||||
photo: "/images/Hobi1.jpg",
|
||||
alt: "saya mengambil foto dari google karena foto foto saya sudah hilang",
|
||||
description:
|
||||
"Saya suka bermain voli karena menurut saya cocok dengan saya",
|
||||
details: [
|
||||
"Pemanasan sebelum bermain",
|
||||
"belajar form gerakan dengan benar",
|
||||
"dan yang terpenting makan dulu sebelum bermain",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "GYM",
|
||||
photo: "/images/Hobi2.jpeg",
|
||||
alt: "Saya Mirror Selfie",
|
||||
description:
|
||||
"Ngegym agar tubuh lebih bugar.",
|
||||
details: [
|
||||
"Belajar form gerakan yang benar",
|
||||
"Menjaga pola makan",
|
||||
"Mengatur waktu tidur",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Makan Bakso",
|
||||
photo: "/images/Hobi3.jpeg",
|
||||
alt: "Semangkuk bakso yang ada di manado",
|
||||
description:
|
||||
"Salah satu tempat bakso yang recomended di manado",
|
||||
details: [
|
||||
"Meracik bakso ini agar lebih enak",
|
||||
"Tambah 1 porsi karena kurang kalau cuma 1",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const gallery = [
|
||||
{ src: "/images/Galeri1.jpeg", alt: "Suasana kampus pada pagi hari", caption: "Pagi di kampus" },
|
||||
{ src: "/images/Galeri2.jpeg", alt: "Langit sore di kantin kampus", caption: "Langit sore surabaya" },
|
||||
{ src: "/images/Galeri3.jpeg", alt: "Lagi di tempat rental ps", caption: "Bermain PS" },
|
||||
{ src: "/images/Galeri4.jpeg", alt: "suasana surabaya bagian barat", caption: "Truk tiduran" },
|
||||
{ src: "/images/Galeri5.jpeg", alt: "motor terlantar", caption: "Harga tertinggi angkut" },
|
||||
{ src: "/images/Galeri6.jpeg", alt: "Olahraga sore", caption: "Kaka adik lagi main bareng" },
|
||||
];
|
||||
|
||||
export const contacts = [
|
||||
{ label: "Email", text: "rmoniaga007@gmail.com", href: "mailto:nama@email.com", external: false },
|
||||
{ label: "Instagram", text: "graymoniaga", href: "https://www.instagram.com/", external: true },
|
||||
];
|
||||
@@ -0,0 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference path="./.next/types/routes.d.ts" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "personal-website",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^15.1.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.17.6",
|
||||
"@types/react": "19.3.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"typescript": "5.8.2"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 73 KiB |
@@ -0,0 +1,197 @@
|
||||
/* stylesheet eksternal: dipakai oleh hobbies, about (nav + wrapper), dan contact */
|
||||
/* semua selector memakai kelas supaya tetap menang atas reset tailwind di about */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
color: #1c2b33;
|
||||
background: #f3f7f7;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px 24px;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 16px 24px 0;
|
||||
}
|
||||
|
||||
.brand {
|
||||
margin: 0;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: #0f4c5c;
|
||||
}
|
||||
|
||||
/* navigasi */
|
||||
.site-nav {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.site-nav ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.site-nav a {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
border-radius: 999px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
color: #0f4c5c;
|
||||
background: #e3eef0;
|
||||
}
|
||||
|
||||
.site-nav a:hover {
|
||||
background: #cfe2e6;
|
||||
}
|
||||
|
||||
.site-nav a.active {
|
||||
color: #ffffff;
|
||||
background: #0f4c5c;
|
||||
}
|
||||
|
||||
/* tipografi */
|
||||
.container h1 {
|
||||
margin: 24px 0 12px;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-size: 2.4rem;
|
||||
line-height: 1.15;
|
||||
color: #0f4c5c;
|
||||
}
|
||||
|
||||
.container h2 {
|
||||
margin: 0 0 8px;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-size: 1.5rem;
|
||||
color: #0f4c5c;
|
||||
}
|
||||
|
||||
.container h3 {
|
||||
margin: 16px 0 6px;
|
||||
font-size: 1.05rem;
|
||||
color: #1c2b33;
|
||||
}
|
||||
|
||||
.container p {
|
||||
margin: 0 0 12px;
|
||||
line-height: 1.7;
|
||||
max-width: 65ch;
|
||||
}
|
||||
|
||||
.container ul,
|
||||
.container ol {
|
||||
margin: 0 0 12px;
|
||||
padding-left: 24px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.container ul {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
.container ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.container a {
|
||||
color: #0f4c5c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* blok konten */
|
||||
.panel {
|
||||
margin-bottom: 24px;
|
||||
padding: 24px;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
border-left: 6px solid #0f4c5c;
|
||||
}
|
||||
|
||||
/* hobbies */
|
||||
.hobby {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 24px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 24px;
|
||||
padding: 24px;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
border-left: 6px solid #e09f3e;
|
||||
}
|
||||
|
||||
.hobby img {
|
||||
width: 260px;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.hobby-text {
|
||||
flex: 1 1 280px;
|
||||
}
|
||||
|
||||
/* tabel */
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.container .data-table th,
|
||||
.container .data-table td {
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #c9d8db;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.container .data-table th {
|
||||
background: #0f4c5c;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* kontak */
|
||||
.contact-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container .contact-list {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.contact-list li {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #e3eef0;
|
||||
}
|
||||
|
||||
.contact-label {
|
||||
display: inline-block;
|
||||
min-width: 110px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* footer */
|
||||
.site-footer {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 16px 24px 32px;
|
||||
border-top: 1px solid #c9d8db;
|
||||
font-size: 0.9rem;
|
||||
color: #4a5d66;
|
||||
}
|
||||
|
||||
.site-footer p {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [{ "name": "next" }]
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||