メインコンテンツへスキップ
← 記事一覧に戻る
·開発·12 min read

Wake Lock API でスマホが消えない「料理モード」を作る

recipe-aiWake Lock APIUXPWA個人開発

料理中にスマホでレシピを見ていたら、画面が消えた。手は油まみれ。電源ボタンを肘で押して、ロック解除して、またブラウザに戻って――この体験、誰もが一度はやっているはずです。

recipe-ai では「料理モード」という機能でこの問題を解決しました。Screen Wake Lock API で画面スリープを抑制し、キーボード操作でステップを進められる全画面UIを実装しています。この記事では、その実装をコード付きで全公開します。

この記事でわかること:

  • Screen Wake Lock API の基本と対応ブラウザ
  • 料理中に最適化されたフルスクリーンUIの設計判断
  • キーボードナビゲーションで手を使わずにステップを進める方法
  • Wake Lock が使えないブラウザでのフォールバック処理

料理中の「画面消える問題」

料理中のスマホ操作には、デスクワークとはまったく異なる制約があります。

  • 手が濡れている・汚れている -- 小麦粉、油、生肉を触った手でスマホには触りたくない
  • タッチ操作が困難 -- 濡れた指ではタッチパネルが反応しにくい
  • 頻繁に画面を確認する -- 「次の手順なんだっけ」を何度も見返す
  • 画面スリープが短い -- 多くのスマホは30秒〜1分で画面が消える

設定アプリからスリープ時間を延ばす方法もありますが、料理のためだけに設定を変えて、終わったら戻す――そんな面倒なことは誰もしません。アプリ側で解決すべき問題です。

Wake Lock API とは

Screen Wake Lock API は、Webページがデバイスの画面スリープを一時的に抑制できるブラウザAPIです。ネイティブアプリでなくても、ブラウザだけで画面の常時点灯を実現できます。

基本的な使い方は非常にシンプルです。

// Wake Lock を取得(画面スリープを抑制)
const wakeLock = await navigator.wakeLock.request("screen");

// 不要になったら解放
await wakeLock.release();

navigator.wakeLock.request("screen") を呼ぶと、ブラウザはOSに対して「このページが表示されている間は画面を消さないでほしい」とリクエストします。返ってくる WakeLockSentinel オブジェクトの release() を呼べば、抑制を解除できます。

重要なのは、ユーザーがタブを切り替えたり、ブラウザを最小化したりすると、Wake Lock は自動的に解放されるという点です。バッテリーを無駄に消費し続ける心配はありません。

recipe-ai の「料理モード」実装

recipe-ai では、レシピカードの「調理モード」ボタンを押すと全画面の料理モードに切り替わります。

料理モードの起動

RecipeCard コンポーネントで、ボタンクリックにより CookingMode を表示します。

// RecipeCard.tsx(抜粋)
const [cookingOpen, setCookingOpen] = useState(false);

// 手順セクションのヘッダーに配置
<button
  type="button"
  onClick={() => setCookingOpen(true)}
  className="text-xs border border-stone-900 text-stone-900 px-3 py-1
             hover:bg-stone-900 hover:text-white transition-colors"
>
  ▶ 調理モード
</button>

// 条件付きレンダリング
{cookingOpen && (
  <CookingMode recipe={recipe} onClose={() => setCookingOpen(false)} />
)}

Wake Lock の取得と解放

CookingMode コンポーネントのコア部分です。マウント時に Wake Lock を取得し、アンマウント時に解放します。

// CookingMode.tsx — Wake Lock 処理
useEffect(() => {
  let sentinel: WakeLockSentinel | null = null;
  const nav = navigator as Navigator & {
    wakeLock?: { request: (type: "screen") => Promise<WakeLockSentinel> };
  };
  if (nav.wakeLock) {
    nav.wakeLock
      .request("screen")
      .then((s) => {
        sentinel = s;
      })
      .catch(() => {});
  }
  return () => {
    sentinel?.release().catch(() => {});
  };
}, []);

ここでのポイントは3つあります。

  1. 型アサーション -- TypeScript の型定義では navigator.wakeLock が存在しない環境もあるため、オプショナルプロパティとして型を拡張しています
  2. 存在チェック -- if (nav.wakeLock) で API の有無を確認してから呼び出し。非対応ブラウザではこのブロックがスキップされるだけで、料理モード自体は正常に動作します
  3. クリーンアップ -- useEffect の return で sentinel?.release() を呼び、コンポーネント破棄時に確実に解放。.catch(() => {}) で、すでに解放済みの場合のエラーを握りつぶしています

フルスクリーン表示

料理モードはダークテーマの全画面オーバーレイとして表示します。キッチンの照明下でも視認性が高く、現在のステップだけに集中できる設計です。

<div className="fixed inset-0 z-50 bg-stone-900 text-white flex flex-col">
  <header className="flex items-center justify-between px-4 py-3
                      border-b border-stone-700 shrink-0">
    <div className="min-w-0 flex-1">
      <p className="text-xs text-stone-400 truncate">{recipe.title}</p>
      <p className="text-sm font-bold tabular-nums">
        Step {step + 1} / {total}
      </p>
    </div>
    <button onClick={onClose} className="text-stone-300 hover:text-white px-3 py-2 text-sm"
            aria-label="終了">
      ✕ 終了
    </button>
  </header>

  <main className="flex-1 flex items-center justify-center px-6 py-8 overflow-y-auto">
    <p className="text-2xl md:text-4xl leading-relaxed text-center max-w-2xl">
      {current}
    </p>
  </main>
</div>

fixed inset-0 z-50 で画面全体を覆い、bg-stone-900 のダーク背景に白文字。本文は text-2xl md:text-4xl で大きく表示し、キッチンから少し離れた位置でも読めるようにしています。

また、料理モード表示中はスクロールをロックしています。

useEffect(() => {
  const prev = document.body.style.overflow;
  document.body.style.overflow = "hidden";
  return () => {
    document.body.style.overflow = prev;
  };
}, []);

キーボードナビゲーション

手が汚れている状況では、画面タッチよりもキーボード操作のほうが現実的です。外付けキーボードや、Bluetoothリモコンのような物理デバイスで操作できるよう、キーボードイベントを実装しています。

useEffect(() => {
  function onKey(e: KeyboardEvent) {
    if (e.key === "Escape") onClose();
    if (e.key === "ArrowRight" || e.key === " ")
      setStep((s) => Math.min(s + 1, total - 1));
    if (e.key === "ArrowLeft") setStep((s) => Math.max(s - 1, 0));
  }
  window.addEventListener("keydown", onKey);
  return () => window.removeEventListener("keydown", onKey);
}, [total, onClose]);

操作体系はシンプルに3つだけです。

キー動作
→ / Space次のステップへ
前のステップへ
ESC料理モード終了

スペースキーを「次へ」に割り当てているのは、キーボードを見なくても大きなキーを叩けるからです。料理中にキーの位置を探す余裕はありません。

ステップ進行UI

画面上部にはプログレスバーを表示し、全体の進捗が一目でわかるようにしています。

const progress = ((step + 1) / total) * 100;

<div className="h-1 bg-stone-800 shrink-0">
  <div
    className="h-full bg-white transition-all duration-300"
    style={{ width: `${progress}%` }}
  />
</div>

画面下部のナビゲーションボタンは、タッチ操作も考慮した大きめのサイズです。最後のステップでは「次へ」が「完了」に変わり、押すと料理モードが終了します。

<footer className="flex gap-3 p-4 border-t border-stone-700 shrink-0 safe-bottom">
  <button
    onClick={() => setStep((s) => Math.max(s - 1, 0))}
    disabled={step === 0}
    className="flex-1 py-4 text-lg font-medium border border-stone-600
               hover:bg-stone-800 disabled:opacity-30 disabled:cursor-not-allowed"
  >
    ← 前
  </button>
  <button
    onClick={() => {
      if (step >= total - 1) onClose();
      else setStep((s) => s + 1);
    }}
    className="flex-[2] py-4 text-lg font-bold bg-white text-stone-900
               hover:bg-stone-200"
  >
    {step >= total - 1 ? "完了 ✓" : "次へ →"}
  </button>
</footer>

「次へ」ボタンを flex-[2] で「前」ボタンの2倍の幅にしているのは、料理中は前に戻るより先に進む操作が圧倒的に多いからです。safe-bottom クラスでiPhoneのホームインジケーター領域との干渉も回避しています。

対応ブラウザと注意点

Screen Wake Lock API の対応状況(2026年4月時点)は以下のとおりです。

ブラウザ対応状況
Chrome (Android/Desktop)84以降で対応
Safari (iOS/macOS)16.4以降で対応
Firefox未対応
Samsung Internet対応

実用上はほぼ問題ありません。 iOS Safari と Android Chrome が対応しているため、スマホでレシピを見るユーザーの大多数をカバーできます。

Firefox は2026年4月時点でも未対応ですが、recipe-ai の実装では Wake Lock が使えなくても料理モード自体は正常に動作します。画面スリープの抑制だけが効かないという差分に留まります。

// 非対応ブラウザでは if ブロックがスキップされるだけ
if (nav.wakeLock) {
  nav.wakeLock.request("screen").then(/* ... */).catch(() => {});
}
// → 料理モードのUI・キーボード操作・ステップ進行はすべて動く

これが「Graceful Degradation(優雅な劣化)」です。機能が使えない環境でもアプリが壊れない。Web開発の基本ですが、新しいAPIを使うときほど意識すべき設計方針です。

Wake Lock API の実装は10行程度のコードで完結します。対して、ユーザーが「画面が消えない」と感じる体験改善のインパクトは大きい。コスト対効果が非常に高い機能です。新しいブラウザAPIを追いかける習慣があると、こういう小さな改善を積み重ねられます。

まとめ

Wake Lock API は数行のコードで「画面が消えない」体験を実現できる。料理モードでは、ダークテーマの全画面表示とキーボードナビゲーションを組み合わせることで、手が汚れていても快適にレシピを進められるUIを作った。非対応ブラウザでもモード自体は動作するフォールバック設計がポイント。

recipe-ai を試す(無料)

この記事で作っている YouTube 料理レシピ抽出アプリ本体。動画 URL を貼るだけで AI がレシピに変換。月 5 本まで無料。

関連記事

Gemini 2.5 Flash で YouTube動画からレシピを自動抽出する方法

recipe-aiの技術スタックとAI実装の全容

Claude Code で 0→MVP を1日で作る全記録

recipe-ai Build in Public の全工程を公開

Claude Crew Lab Free — 毎月の実験記録をメールで

Claude Code × 個人開発のリアルな事故・発見・SaaS アイデアを毎月第1月曜にお届け。登録で「収益化チェックリスト 15 項目」を無料プレゼント。

Lab Free 登録(月1回・無料)

この記事が役に立ったらシェア