Timer hitung mundur sederhana dan ringan, dibangun dengan Next.js 16 (App Router) + Tailwind CSS v4. Tanpa dependensi tambahan di luar bawaan Next — tidak ada state library, tidak ada komponen UI pihak ketiga.
- Preset 1 / 3 / 5 / 10 / 25 menit
- Start / Pause / Resume / Restart dan Reset
- Cincin progres SVG yang menyusut mengikuti sisa waktu
- Alarm 3× beep 880 Hz saat waktu habis, plus angka berkedip
- Judul tab menampilkan sisa waktu selama timer berjalan
- Ikut
prefers-color-scheme— terang/gelap otomatis
npm run dev # http://localhost:3000Untuk membuka dari perangkat lain di jaringan yang sama (HP, laptop lain):
npm run dev:lanScript ini mendeteksi alamat LAN mesin dan menampilkannya sebelum dev server menyala:
Lokal http://localhost:3000
Perangkat lain http://192.168.137.121:3000 (dari ip route get)
| Tombol | Aksi |
|---|---|
Space |
Mulai / jeda — selalu, termasuk saat fokus sedang di sebuah tombol |
R |
Reset ke durasi penuh |
app/
layout.tsx metadata + font
page.tsx merender <Timer />
timer.tsx seluruh logika dan tampilan timer (client component)
globals.css token warna, tema terang/gelap, animasi
scripts/
dev-lan.mjs deteksi alamat LAN lalu jalankan next dev
next.config.ts allowedDevOrigins dideteksi otomatis dari interface mesin
Dua hal yang sengaja ditangani supaya timer-nya benar-benar akurat:
Tanpa drift. Hitung mundur dihitung dari target timestamp (endAt - Date.now()), bukan dengan mengurangi counter setiap tick. Interval yang tersendat — misalnya karena tab tidak aktif — tidak menumpuk error, karena setiap tick membaca ulang jam.
Alarm tetap bunyi saat tab di background. Tab yang tersembunyi men-throttle setInterval sampai kira-kira sekali per menit, jadi beep tidak bisa diandalkan kalau dijadwalkan lewat JS timer. Bunyinya dijadwalkan langsung ke jam AudioContext saat Start ditekan, dan dibatalkan saat Pause/Reset. Yang di-throttle hanya pembaruan tampilan — itu pun langsung terkoreksi begitu tab dibuka lagi, karena state-nya juga dihitung dari timestamp.
Sejak Next 16, permintaan ke resource dev (/_next/*, HMR) dari origin lain diblokir secara default. Gejalanya menyesatkan: halaman tampil normal karena HTML-nya dirender server, tetapi chunk JS-nya ditolak 403 — React tidak pernah hidrasi dan semua tombol diam.
next.config.ts mendaftarkan alamat LAN mesin dengan membacanya dari networkInterfaces() saat config dimuat:
for (const [name, addrs] of Object.entries(networkInterfaces())) {
if (VIRTUAL_IFACE.test(name)) continue; // buang docker0, vpn, dll
for (const addr of addrs ?? []) {
if (addr.family === "IPv4" && !addr.internal) origins.add(addr.address);
}
}Karena dibaca saat start, IP dari DHCP boleh berubah tanpa menyunting apa pun — termasuk kalau dijalankan dengan npm run dev biasa.
Kalau perlu menambahkan hostname mDNS atau IP statis:
ALLOWED_DEV_ORIGINS=mesinku.local,10.0.0.5 npm run dev:lanNext membaca config sekali saat start, jadi kalau jaringan berpindah (mis. Wi-Fi → hotspot) saat server sedang jalan, jalankan ulang perintahnya.
Jaringan di mesin ini lambat dan tidak stabil. Koneksi ke GitHub tetap jalan — push pertama butuh sekitar 30 detik — tetapi registry.npmjs.org konsisten timeout: curl gagal pada detik ke-20, dan npm install menggantung lebih dari 10 menit lalu berakhir dengan network error, setelah lebih dulu menghapus node_modules. Jadi jangan menjalankan npm install di sini.
node_modules diisi dari project lokal lain yang sudah punya Next 16, jadi versinya next@16.3.5 (bukan 16.3.8 yang dipilih create-next-app). Versi di package.json sengaja dipatok agar cocok dengan tree tersebut. Konsekuensinya node_modules masih berisi beberapa paket sisa project sumber (supabase, jose, server-only) yang tidak dipakai — tidak berpengaruh ke build, dan bisa dibersihkan dengan npm install begitu jaringan tersedia.
Verifikasi tanpa install:
npm run buildBisa dideploy ke Vercel. Halaman / sepenuhnya statis, jadi tidak ada konfigurasi khusus — allowedDevOrigins hanya berlaku di mode development dan diabaikan saat produksi.