ekel.dev is Haikel Ilham Hakim's statically generated personal website. It publishes professional experience, selected work, technical notes, photography, and a few interactive utilities while keeping most pages pre-rendered at build time.
| Concern | Implementation |
|---|---|
| Web framework | Astro 6, static output |
| Interactive islands | Svelte 5 |
| Styling | Tailwind CSS 4 and custom CSS |
| Shared UI | shadcn-svelte primitives in packages/ui |
| Content | Typed Astro collections with MDX |
| Fonts | Geist Sans, Geist Mono, and Noto Naskh Arabic |
| Workspace | Bun workspaces and Moon |
| Quality | Astro Check, Biome, Cypress, Lighthouse CI |
| Monitoring | Sentry |
Astro owns routes, layouts, content rendering, and static generation. Svelte is reserved for components that need client-side interaction, such as theme, navigation, charts, reactions, and photo controls.
/— biography, experience, featured work, and latest notes/experiences/<slug>— professional experience details/worksand/works/<slug>— project portfolio/notesand/notes/<slug>— writing/tagsand/tags/<slug>— note indexes by tag/photos— photography/guestbook— visitor messages/now— current activities/uses— hardware and software/wakatime— coding activity/ihsg— stock-index viewer
There is intentionally no /experiences index route. Content routes are built
from records in apps/web/src/content, not duplicated in page modules.
ikuyo/
├── apps/web/
│ ├── cypress/ # Browser-level behavior and accessibility
│ └── src/
│ ├── components/ # Site-specific Astro and Svelte components
│ ├── content/ # Notes, works, and experiences as MDX
│ ├── content.config.ts # Collection schemas and loaders
│ ├── layouts/ # Shared page shell
│ ├── pages/ # Astro file-based routes
│ ├── styles/ # Global theme, prose, and utility CSS
│ └── utils/ # Content, environment, and rendering helpers
├── packages/ui/ # Shared Svelte UI primitives
├── docs/ # Product contracts and Harness documentation
├── scripts/ # Harness bootstrap, CLI, and schema
├── AGENTS.md # Agent entrypoint for every request
└── DESIGN.md # Visual-system contract
Requirements:
- Bun
- Node.js 22.16.0 (pinned by
.node-versionfor Astro and Cloudflare Pages)
Install dependencies and start the Astro development server:
bun install
bun run dev:webThe site is available at http://localhost:3000.
Run commands from the repository root unless noted otherwise.
bun run dev:web # Start the web development server
bun run --cwd apps/web build # Astro check and production build
bun run build:web # Format workspaces, then build through Moon
bun run lint:biome # Check web and shared UI source
bun run format:biome # Format web and shared UI source
bun run test:web # Start the site and run all Cypress tests
bun run lhci:mobile # Lighthouse CI mobile preset
bun run lhci:desktop # Lighthouse CI desktop presetbuild:web writes formatting changes before building. Use
bun run --cwd apps/web build when you need a non-formatting build check.
Cloudflare Pages infrastructure is declared under infra/cloudflare. Terraform
adopts the existing project, custom domain, and optional DNS record; GitHub
Actions builds the site and Wrangler uploads apps/web/dist. The rollout starts
with manual imports, a reviewed Terraform plan, and a manual deployment to avoid
interrupting the existing Git-based deployment.
Follow the step-by-step operator guide in infra/cloudflare/README.md. Never
commit Terraform state or Cloudflare API tokens, and do not run terraform apply until imported resources produce a no-replacement plan.
Copy the example file before using integrations that require credentials:
cp apps/web/.env.example apps/web/.envThe example declares production/development URLs, Turso credentials, Sentry, Lighthouse CI, ImageKit, and Cloudflare values. Keep secrets out of version control. The static content pages can be developed without every external integration configured, but affected integrations need their matching values.
Collection contracts live in apps/web/src/content.config.ts. Add or update an
MDX record under the matching collection:
apps/web/src/content/notesapps/web/src/content/worksapps/web/src/content/experiences
Dynamic routes use Astro getStaticPaths, getCollection, and render to
produce one static page per record. Update the collection schema rather than
adding untyped or duplicated page data.
This repository includes a local engineering Harness that turns requests into
bounded, validated work. AGENTS.md is the entrypoint. Human-readable policy
and product truth live in docs; operational records live in the ignored
harness.db through scripts/bin/harness-cli.
For change requests, the workflow is:
intent → intake and risk lane → story/proof scope → implementation
→ validation → trace → captured friction
Bootstrap local Harness state when needed:
scripts/bootstrap-harness.sh
scripts/bin/harness-cli query matrix --active --summaryRead docs/README.md and docs/HARNESS.md for the full workflow. Read-only
requests do not mutate Harness state; build, fix, and edit requests record
intake and a completion trace.
MIT