Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

54 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Arsitektur Website β€” webmd

Personal portfolio + blog + dokumentasi, built with Express.js + EJS + Tailwind CSS.


πŸ“ Struktur Proyek

webmd/
β”œβ”€β”€ content/                   # Markdown sumber konten
β”‚   β”œβ”€β”€ blog/*.md              # Artikel blog (10 file)
β”‚   └── docs/*.md              # Dokumentasi (2 file)
β”œβ”€β”€ data/                      # Data konfigurasi JSON
β”‚   β”œβ”€β”€ config.json            # Info situs & profil
β”‚   β”œβ”€β”€ navigation.json        # Menu navbar
β”‚   β”œβ”€β”€ socials.json           # Link sosial media
β”‚   └── projects.json          # Data proyek
β”œβ”€β”€ public/                    # File statis (gambar, favicon)
β”œβ”€β”€ src/                       # Kode backend
β”‚   β”œβ”€β”€ app.js                 # Entry point Express
β”‚   β”œβ”€β”€ controllers/
β”‚   β”‚   β”œβ”€β”€ pageController.js  # Handler halaman (home, blog, docs)
β”‚   β”‚   └── projectController.js # API projects
β”‚   β”œβ”€β”€ routes/
β”‚   β”‚   β”œβ”€β”€ index.js           # Setup routing
β”‚   β”‚   └── pages.js           # Definisi route
β”‚   β”œβ”€β”€ middlewares/
β”‚   β”‚   β”œβ”€β”€ locals.js           # Inject data ke semua view
β”‚   β”‚   └── errorHandler.js    # 404 & 500 handler
β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”œβ”€β”€ cache.js           # Cache in-memory singleton
β”‚   β”‚   β”œβ”€β”€ contentLoader.js   # Load data & markdown saat startup
β”‚   β”‚   β”œβ”€β”€ parser.js          # Parse markdown β†’ HTML via markdown-it
β”‚   β”‚   β”œβ”€β”€ markdown.js        # Config markdown-it + plugin registration
β”‚   β”‚   └── seo.js             # Helper meta tag SEO
β”‚   └── libs/markdown/         # Plugin kustom markdown-it (8 plugin)
β”‚       β”œβ”€β”€ GithubAnchor.js
β”‚       β”œβ”€β”€ TableOfContent.js
β”‚       β”œβ”€β”€ AdmonitionContainer.js
β”‚       β”œβ”€β”€ MacCodeBlock.js
β”‚       β”œβ”€β”€ MultiMediaPlayer.js
β”‚       β”œβ”€β”€ HighlightBacktick.js
β”‚       β”œβ”€β”€ Tabs.js
β”‚       └── Dokapi.js
β”œβ”€β”€ views/                     # Template EJS
β”‚   β”œβ”€β”€ layouts/
β”‚   β”‚   └── base.ejs           # Layout utama (html, head, body, navbar, footer)
β”‚   β”œβ”€β”€ pages/
β”‚   β”‚   β”œβ”€β”€ home.ejs           # Halaman depan
β”‚   β”‚   β”œβ”€β”€ blog.ejs           # Index blog
β”‚   β”‚   β”œβ”€β”€ post.ejs           # Detail artikel blog
β”‚   β”‚   β”œβ”€β”€ docs.ejs           # Index dokumentasi
β”‚   β”‚   β”œβ”€β”€ doc.ejs            # Detail dokumentasi
β”‚   β”‚   β”œβ”€β”€ 404.ejs            # Halaman tidak ditemukan
β”‚   β”‚   └── 500.ejs            # Error server
β”‚   └── partials/
β”‚       β”œβ”€β”€ seo.ejs            # Meta tag SEO (OG, Twitter, article)
β”‚       β”œβ”€β”€ navbar.ejs         # Navigasi sticky
β”‚       β”œβ”€β”€ footer.ejs         # Footer
β”‚       β”œβ”€β”€ post-card.ejs      # Card artikel
β”‚       └── project-card.ejs   # Card proyek
β”œβ”€β”€ vercel.json                # Config deploy Vercel
β”œβ”€β”€ package.json
β”œβ”€β”€ readme.md
β”œβ”€β”€ DESIGN-wise.md             # Design system Wise-inspired
└── ARSITEKTUR.md              ← file ini

βš™οΈ Cara Kerja (Request β†’ Response)

Request masuk
  β”‚
  β”œβ”€ helmet() β†’ security headers
  β”œβ”€ compression() β†’ gzip
  β”œβ”€ express.static('/static') β†’ file di public/
  β”œβ”€ render wrapper β†’ nangkap res.render(), render page β†’ bungkus di layouts/base.ejs
  β”œβ”€ initialization guard β†’ tunggu data selesai di-load (cold start)
  β”œβ”€ injectLocals β†’ navigation, socials, config, currentPath β†’ res.locals
  β”‚
  β”œβ”€ Router (pages.js)
  β”‚   β”œβ”€ GET /           β†’ renderHome
  β”‚   β”œβ”€ GET /blog       β†’ renderBlogIndex
  β”‚   β”œβ”€ GET /blog/:slug β†’ renderBlogPost
  β”‚   β”œβ”€ GET /docs       β†’ renderDocsIndex
  β”‚   β”œβ”€ GET /docs/:slug β†’ renderDocDetail
  β”‚   └─ GET /api/projects β†’ apiProjects (JSON)
  β”‚
  β”œβ”€ notFound (404) jika tidak ada route cocok
  └─ serverError (500) jika ada exception

Cold Start vs Warm Start

  • Cold start (pertama kali setelah deploy/idle): initializeApplicationData() jalan β€” baca semua file JSON + glob markdown + parse semuanya sekali β†’ simpan di cache in-memory.
  • Warm start (request berikutnya): guard if (cache.blogs.length && cache.docs.length) return β€” skip, langsung pakai cache.
  • Guard middleware pakai initPromise agar request kedua menunggu request pertama selesai, bukan jalan duplikat.

🎨 Dimana Mengganti Style

Tidak ada file CSS statis. Semua styling via Tailwind CDN + inline class di EJS. Ini tempatnya:

1. Tailwind Config β€” views/layouts/base.ejs

<script>
  tailwind.config = {
    darkMode: 'class',
    theme: {
      extend: {
        fontFamily: { sans: ['Inter', ...] },
        animation: { fadeIn: ... },
        keyframes: { fadeIn: ... }
      }
    }
  }
</script>

Ganti font, animasi, atau kustomisasi Tailwind di sini.

2. Warna Tema (light/dark) β€” views/layouts/base.ejs

Di bagian <style> inline:

/* Prose colors (light) */
.prose { --tw-prose-body: #454745; --tw-prose-headings: #0e0f0c; ... }

/* Prose colors (dark) */
.dark .prose { --tw-prose-body: #c8cac5; --tw-prose-headings: #e8ebe6; ... }

/* Syntax highlighting Prism (light & dark) */
.token.comment { color: #868685; ... }
.dark .token.comment { color: #6b6b6a; ... }

3. Warna Background & Teks Global β€” views/layouts/base.ejs

<body class="bg-[#e8ebe6] dark:bg-[#0e0f0c] text-[#0e0f0c] dark:text-[#e8ebe6] ...">

4. Komponen Per-Halaman β€” Masing-masing file .ejs di views/pages/ & views/partials/

File Yang di-style
views/pages/home.ejs Hero section, glow background, cards "Operational DNA", featured projects, latest posts
views/pages/blog.ejs Search bar, sort dropdown, tag chips, grid post-card, modal "More tags"
views/pages/post.ejs Cover image, meta info, tags, konten artikel (prose)
views/pages/docs.ejs Grid card dokumentasi
views/pages/doc.ejs Layout 2 kolom (artikel + sidebar), konten prose
views/pages/404.ejs Angka besar 404, tombol Go Home
views/pages/500.ejs Angka besar 500, error stack (dev mode)
views/partials/navbar.ejs Sticky nav, logo, menu desktop/mobile, toggle dark mode
views/partials/footer.ejs Copyright, sosial icons
views/partials/post-card.ejs Card artikel (cover, title, desc, tags)
views/partials/project-card.ejs Card proyek (cover, title, desc, tags)

5. Plugin Markdown β€” Masing-masing file di src/libs/markdown/

Plugin menghasilkan HTML dengan class Tailwind inline (server-side). Contoh:

Plugin File Class Tailwind yang dipakai
GithubAnchor GithubAnchor.js group-hover:opacity-100, no-underline
TableOfContent TableOfContent.js bg-white/50, backdrop-blur, border-sky-200, text-sky-700
AdmonitionContainer AdmonitionContainer.js border-l-4, bg-sky-50, text-sky-800, dark:...
MacCodeBlock MacCodeBlock.js rounded-2xl, bg-[#1e1e1e], shadow-lg
MultiMediaPlayer MultiMediaPlayer.js class tailwind di wrapper video/audio
HighlightBacktick HighlightBacktick.js bg-cyan-50, text-cyan-800, shadow-inner
Tabs Tabs.js border-b-2, border-sky-500, text-sky-600
Dokapi Dokapi.js Badge warna per HTTP method, collapsible card

6. Data JSON β€” data/

File Fungsinya
config.json siteName, siteTitle, siteDescription, siteUrl, avatar, fullName, occupation, bio
navigation.json Array {label, href} untuk navbar
socials.json Array {name, icon, url} untuk footer
projects.json Array proyek dengan featured boolean

7. Konten Markdown β€” content/blog/*.md & content/docs/*.md

Frontmatter (YAML):

---
title: "Judul"
description: "Deskripsi"
slug: "judul-artikel"
tags: ["tag1", "tag2"]
createdAt: "2025-07-01"
updatedAt: "2025-07-05"
written: "Nama Penulis"
pinned: false
draft: false
cover: "https://..."
---

Kustom syntax markdown: [[toc]], :::note ... :::, ;;tabs ... ;;, code block dengan title="", & route ... &, < response ... <, dsb.


🧠 Arsitektur Data (Zero Database)

Semua data dari file β†’ cache in-memory (cold start) β†’ serve dari cache (warm start)

File:
  data/config.json       ─┐
  data/navigation.json   ──  Promise.all() β†’ cache.*
  data/socials.json      ──
  data/projects.json     β”€β”˜
  content/blog/**/*.md   ──  glob β†’ Promise.all(parseMarkdown) β†’ cache.blogs
  content/docs/**/*.md   β”€β”˜                                       β†’ cache.docs

cache = {
  blogs:    [{ title, slug, tags, createdAt, pinned, draft, cover, readingTime, html, ... }],
  docs:     [{ title, slug, tags, createdAt, readingTime, html, ... }],
  projects: [{ id, title, description, image, tags, featured, href }],
  socials:  [{ name, icon, url }],
  navigation: [{ label, href }],
  config:   { siteName, siteTitle, ... }
}

Alur Parsing Markdown

file.md
  β†’ gray-matter (parse frontmatter YAML)
  β†’ markdown-it + 8 plugin kustom β†’ HTML
  β†’ hitung readingTime (200 kata/menit)
  β†’ objek { title, slug, tags, createdAt, ..., html }

πŸš€ Deployment β€” Vercel

vercel.json:

  • Builder: @vercel/node β†’ src/app.js
  • includeFiles: "content/**" β€” markdown ikut bundle
  • Rewrite: /static/* β†’ public/*, sisanya β†’ src/app.js
  • Local dev: node --watch src/app.js (port 3000)

Di Vercel (production), app di-export sebagai default β€” tidak pakai app.listen(). Di lokal (NODE_ENV !== 'production'), app.listen() jalan.


πŸ“ Design System (Wise-inspired)

Dari DESIGN-wise.md β€” palet Wise:

Token Warna Tailwind
primary #9fe870 bg-[#9fe870]
ink #0e0f0c text-[#0e0f0c]
canvas-soft #e8ebe6 bg-[#e8ebe6]
canvas #ffffff bg-white
body #454745 text-[#454745]
mute #868685 text-[#868685]

Semua warna hardcoded sebagai bg-[#...] atau text-[#...] langsung di class Tailwind. Tidak pakai variable CSS β€” ganti warna dengan search-replace di seluruh file .ejs.

Ceil: Ekstrak ke CSS variables atau Tailwind theme.extend.colors di config kalau warna dipakai > 20 kali. Kapan: Saat mau ganti tema secara sistematis.


πŸ”Œ Plugin Markdown Kustom (8 plugin)

Plugin Sintaks Output
GithubAnchor heading ## Teks auto id slug + link anchor #
TableOfContent [[toc]] Dropdown collapsible daftar isi
AdmonitionContainer :::note ... ::: Card berwarna dengan ikon
MacCodeBlock js title="file.js" macOS-style terminal dengan copy & wrap
MultiMediaPlayer ![alt](video.mp4) <video> / <audio> alih-alih <img>
HighlightBacktick `code` Inline code dengan gaya cyan
Tabs ;;tabs ... ;; Tab horizontal dengan animasi fade
Dokapi & route GET /api ... & Card endpoint API collapsible

πŸ§ͺ Routing Lengkap

Method Path Controller View
GET / renderHome pages/home
GET /blog renderBlogIndex pages/blog
GET /blog/:slug renderBlogPost pages/post
GET /docs renderDocsIndex pages/docs
GET /docs/:slug renderDocDetail pages/doc
GET /api/projects apiProjects JSON
* static express.static file di public/
* not found notFound pages/404
* error serverError pages/500

About

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages