まずは完成版を触ってみましょう
時間を選び、「スタート」を押すとカウントダウンが始まります。
🍜 ラーメンタイマーを今すぐ使う今回作るもの
アプリの追加インストールなしで、ChromeやSafariから使えるラーメンタイマーです。
- 3分・4分・5分・7分の時間選択
- スタート、一時停止、再開、リセット
- 残り時間を「03:00」の形で表示
- 終了時の音と振動(対応端末)
- 画面を閉じた間も、時刻差から残り時間を補正
- スマートフォンでも押しやすいボタン
最初から全部を覚える必要はありません。まず基本のカウントダウンを作り、動作を確認してから機能を足すと理解しやすくなります。
実際の公開画面を3枚で確認
トップページの導線からタイマーを開くまでを、実際の画面に沿って確認します。
-
トップ画面へラーメンタイマー導線を追加
Tech Beginner Guideのトップ画面に、既存のデザインに合わせた「🍜 ラーメンタイマー」ボタンを追加しました。「知りたいことを探す」「カテゴリーから探す」と並ぶため、初めて訪れた人でも見つけやすくなります。
① サイトのトップ画面に「🍜 ラーメンタイマー」への導線を追加した状態。 -
押す場所が分かる大きな導線ボタン
ボタンは文字だけのリンクではなく、指でも押しやすい大きさにしています。ラーメンの絵文字と機能名を組み合わせ、リンク先が一目で分かるようにしました。
② 導線ボタンの拡大。ほかの操作ボタンと同じ角丸・余白で、既存デザインになじませています。 -
完成した赤いラーメンタイマーを表示
導線を押すと、赤い背景のタイマー画面が開きます。時間選択、残り時間、スタート、リセットを1画面に集め、調理中でも迷いにくい構成です。
③ 完成した赤いラーメンタイマー。3分・4分・5分・7分を選び、スタートまたはリセットできます。
Webアプリは3つの技術で作れる
ラーメン店に例えるなら、HTMLが店舗、CSSが内装、JavaScriptが店員さんの動きです。3つを同じHTMLファイルへ書けば、最初の練習ではファイル管理も簡単です。
1ファイルで動く基本コード
次の内容をコピーし、ramen-timer.htmlという名前で保存します。
学習しやすいように基本機能へ絞ったコードです。公開中の完成版は、ここへ音・振動・画面復帰時の時間補正を追加しています。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>ラーメンタイマー</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 20px;
font-family: system-ui, sans-serif;
background: repeating-linear-gradient(
45deg, #e43d2f 0 18px, #d83227 18px 36px
);
}
.timer {
width: min(100%, 520px);
padding: 28px 20px;
text-align: center;
background: #fff9e8;
border: 5px solid #24140d;
border-radius: 28px;
box-shadow: 0 16px 45px #0006;
}
h1 { margin: 0 0 12px; }
#time {
margin: 20px 0;
font-size: clamp(70px, 20vw, 108px);
line-height: 1;
font-weight: 900;
font-variant-numeric: tabular-nums;
}
.presets, .controls {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 10px;
margin-top: 14px;
}
button {
min-height: 48px;
padding: 12px 18px;
border: 3px solid #24140d;
border-radius: 14px;
background: #fff1c8;
font-size: 17px;
font-weight: 900;
cursor: pointer;
box-shadow: 0 4px 0 #24140d;
}
button:active {
transform: translateY(4px);
box-shadow: none;
}
.start { background: #c92720; color: #fff; }
.active { background: #f4b942; }
#status { min-height: 28px; margin-top: 18px; font-weight: 800; }
</style>
</head>
<body>
<main class="timer">
<h1>🍜 麺、まもなく。</h1>
<div id="time">03:00</div>
<div class="presets">
<button class="preset active" data-seconds="180">3分</button>
<button class="preset" data-seconds="240">4分</button>
<button class="preset" data-seconds="300">5分</button>
<button class="preset" data-seconds="420">7分</button>
</div>
<div class="controls">
<button class="start" id="start">▶ スタート</button>
<button id="reset">↻ リセット</button>
</div>
<div id="status">お湯を入れたらスタート!</div>
</main>
<script>
let selected = 180;
let remaining = selected;
let timerId = null;
const time = document.querySelector("#time");
const start = document.querySelector("#start");
const reset = document.querySelector("#reset");
const status = document.querySelector("#status");
function draw() {
const minutes = Math.floor(remaining / 60);
const seconds = remaining % 60;
time.textContent =
String(minutes).padStart(2, "0") + ":" +
String(seconds).padStart(2, "0");
}
document.querySelectorAll(".preset").forEach((button) => {
button.addEventListener("click", () => {
clearInterval(timerId);
timerId = null;
selected = Number(button.dataset.seconds);
remaining = selected;
document.querySelectorAll(".preset").forEach((item) => {
item.classList.remove("active");
});
button.classList.add("active");
start.textContent = "▶ スタート";
status.textContent = button.textContent + "にセットしました";
draw();
});
});
start.addEventListener("click", () => {
if (timerId !== null) {
clearInterval(timerId);
timerId = null;
start.textContent = "▶ 再開";
status.textContent = "一時停止中";
return;
}
if (remaining === 0) remaining = selected;
start.textContent = "⏸ 一時停止";
status.textContent = "最高の一杯まで、あと少し…";
timerId = setInterval(() => {
remaining--;
draw();
if (remaining <= 0) {
clearInterval(timerId);
timerId = null;
remaining = 0;
draw();
start.textContent = "▶ もう一杯";
status.textContent = "🍜 できあがり!";
}
}, 1000);
});
reset.addEventListener("click", () => {
clearInterval(timerId);
timerId = null;
remaining = selected;
start.textContent = "▶ スタート";
status.textContent = "お湯を入れたらスタート!";
draw();
});
draw();
</script>
</body>
</html>
Macで動かしてみる
- 標準テキストで編集する:テキストエディットを使う場合は「フォーマット」から「標準テキストにする」を選びます。Visual Studio Codeなどのコードエディタでも構いません。
- HTMLとして保存する:上のコードを貼り付け、文字コードをUTF-8にしてramen-timer.htmlで保存します。
- ブラウザで開く:保存したファイルをChromeやSafariへドラッグすると、タイマー画面が表示されます。
- ボタンを試す:4分や5分へ切り替え、スタート、一時停止、再開、リセットを順に確認します。
URL欄にfile:///Users/ユーザー名/...と表示されるのは、インターネット上ではなく、Mac内のファイルをブラウザが直接読んでいるためです。
画面は出るのにボタンが動かない理由
見た目を担当するHTML・CSSが正常でも、JavaScriptに問題があるとボタンだけ反応しません。
idの名前が一致しているか
JavaScriptのdocument.querySelector("#start")は、HTMLからid="start"の要素を探します。HTML側がid="startButton"なら、同じ名前へそろえる必要があります。
JavaScriptを置く位置
ボタンが読み込まれる前にJavaScriptを実行すると、対象を見つけられない場合があります。初心者は、上のコードのように<script>を</body>の直前へ置くと分かりやすいでしょう。
記号の入力ミス
半角の引用符、丸かっこ、波かっこが1つ欠けるだけでも、その後の処理が止まります。Chromeでは開発者ツールの「Console」に赤いエラーがないか確認できます。
タイマーが1秒ずつ減る仕組み
JavaScriptのsetInterval()は、決めた間隔で処理を繰り返します。今回の1000は1000ミリ秒、つまり1秒です。
timerId = setInterval(() => {
remaining--;
draw();
}, 1000);3分は180秒です。Math.floor(remaining / 60)で「分」を、remaining % 60で「秒」を計算します。179秒なら2分59秒なので、画面には「02:59」と表示されます。
各時間ボタンのdata-secondsへ秒数を入れておけば、同じJavaScriptで複数の時間を扱えます。7分を追加する時は420秒です。
HTMLファイルとWeb公開は別の作業
自分のパソコンで動いたHTMLは、まだほかの人がURLから開ける状態ではありません。
Web上で使えるようにするには、HTMLファイルをWebサーバーへ配置します。静的なラーメンタイマーなら、レンタルサーバー、GitHub Pages、Cloudflare Pages、Netlify、Vercelなどで公開できます。
ramen-timer/
└── index.html多くのWebサーバーは、フォルダのURLへアクセスすると中のindex.htmlを自動で表示します。そのため、公開用はramen-timer/index.htmlという構成が便利です。
スマートフォン対応で確認すること
- viewport:端末の画面幅に合わせるため、head内へviewportのmetaタグを入れる
- ボタンの大きさ:指で押しやすい高さと余白を確保する
- 横はみ出し:長いコードや画像だけが画面の外へ飛び出さないようにする
- 背景復帰:画面を閉じた間も正確に測るなら、終了予定時刻との差で残り時間を計算する
- 音と振動:端末やブラウザによって対応が異なるため、表示でも終了を伝える
CSSのclamp()を使うと、大きな残り時間を画面幅に合わせて縮められます。ボタンにはtouch-action: manipulationを指定すると、タップ操作を意識した実装になります。
次に追加できる機能
- 1分から10分までの自由入力
- 終了音の種類と音量選択
- カップ麺の商品別プリセット
- 醤油・味噌・豚骨のテーマ切り替え
- 複数タイマーの同時実行
- PWA化とホーム画面への追加
- QRコードからの起動
一度に全部を追加せず、「1機能を足す → ブラウザで確認する → 問題があれば戻す」という小さな単位で進めるのが安全です。
AIを使って作る時も、最後は実機確認が必要
AIへ「3分・4分・5分を選べる、スマホ対応のラーメンタイマーをHTML・CSS・JavaScriptで作って」と頼めば、コードのたたき台を作れます。
ただし、AIがコードを出したことと、そのコードが実際の環境で動くことは別です。次の順番で確認しましょう。
- AIへ作りたい機能と見た目を具体的に伝える
- 生成されたコードをHTMLファイルへ保存する
- パソコンとスマートフォン幅で表示する
- すべての時間ボタンと操作ボタンを押す
- エラーを直し、もう一度同じ操作を確認する
- Webサーバーへ公開し、実際のURLで再確認する
よくある質問
ラーメンタイマーはアプリのインストールが必要ですか?
必要ありません。HTML・CSS・JavaScriptだけで作られたページなので、ChromeやSafariなどのWebブラウザで利用できます。
HTMLファイルを作ればインターネットで公開されますか?
いいえ。パソコン内のHTMLファイルは自分の端末で開けますが、ほかの人がURLから使うにはWebサーバーへ配置する必要があります。
画面は表示されるのにボタンが動かない原因は何ですか?
JavaScriptの読み込み位置、HTMLとJavaScriptのidの不一致、記号の入力ミスなどを確認します。ブラウザの開発者ツールに表示されるエラーも手掛かりになります。
スマートフォンでも使えますか?
viewport設定と押しやすいボタンサイズを用意すれば使えます。公開版は画面を閉じた時間も時刻差で補正し、対応端末では終了時に音と振動で知らせます。
まとめ
- HTMLで画面、CSSで見た目、JavaScriptでタイマーの動きを作る
- 最初は1つのHTMLファイルにまとめると試しやすい
- 表示されても、すべてのボタンが動くとは限らない
- id、JavaScriptの位置、記号、Consoleのエラーを確認する
- ローカルで動くことと、URLから使えるWeb公開は別
- スマホ幅、押しやすさ、横はみ出し、音が出ない場合も確認する
ラーメンが完成する数分を測るだけの小さなツールでも、立派なWebアプリです。完成版を触ってからコードへ戻ると、それぞれの行が何をしているのか理解しやすくなります。