Compare commits

...
2 Commits
Author SHA1 Message Date
Gray b2e6844781 tugas personal website 2026-11-06 21:37:29 +07:00
Gray bbcd07213c Tugas Personal Website 2026-11-06 21:21:29 +07:00
26 changed files with 1877 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
node_modules
.next
+36
View File
@@ -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.
+118
View File
@@ -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>&ldquo;{quote.text}&rdquo;</p>
<footer>&mdash; {quote.author}</footer>
</blockquote>
</section>
</main>
<footer className="site-footer">
<p>
&copy; {profile.year} {profile.name}
</p>
</footer>
</div>
);
}
+56
View File
@@ -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>
&copy; {profile.year} {profile.name}
</p>
</footer>
</>
);
}
+53
View File
@@ -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">
&copy; {profile.year} {profile.name}
</p>
</footer>
</div>
);
}
+59
View File
@@ -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>
&copy; {profile.year} {profile.name}
</p>
</footer>
</>
);
}
+17
View File
@@ -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>
);
}
+55
View File
@@ -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 }}>
&copy; {profile.year} {profile.name}. Dibuat di {profile.city}.
</p>
</footer>
</div>
);
}
+5
View File
@@ -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} -->` }} />;
}
+80
View File
@@ -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>
);
}
+89
View File
@@ -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 },
];
+6
View File
@@ -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.
+1063
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -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"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

+197
View File
@@ -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;
}
+20
View File
@@ -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"]
}