8
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HTML/CSS/JSでモダンなTODOアプリを開発!レスポンシブ対応・ダークモード・モジュール化まで徹底解説

Last updated at Posted at 2025-07-07

こんにちは!今回は、Web開発の基本であるHTML/CSS/JavaScriptを使って、TODOアプリを開発し、さらにFirebaseで公開するまでの全工程を徹底解説します。

この記事でわかること

この記事を読めば、ただTODOアプリを作るだけでなく、以下のような実践的なWeb開発スキルが身につきます。

  • HTML/CSS/JavaScriptの基礎
  • モダンなWebアプリで必須のレスポンシブデザイン
  • 目に優しいダークモード/ライトモード切り替え
  • 複雑なコードを管理するモジュール化の手法
  • 優先度・カテゴリ管理、一括削除など実践的な機能実装
  • Googleが提供するFirebase Hostingを使ったアプリの簡単公開

コードは全てgithubに掲載しているので、気になる方はチェックして自分の環境でも試してみてください。

完成したアプリのデモと機能紹介

まずは、今回作成したアプリの完成形をご覧ください。
ご覧いただいた上で、自分も作ってみたいと思った方は、こちらの記事と上記のgithubページから作成をしてみてください。
🎉 作成したデモアプリを触ってみる! 🎉
 作成したTODOアプリ

主な機能一覧

  • タスクの追加・完了・編集・削除: 基本的なTODOリスト機能
  • タスクの優先度設定: 「高」「中」「低」「未設定」で優先度を管理
  • タスクのカテゴリ設定・管理: カテゴリを新規作成・削除し、タスクに紐付け可能
  • フィルタリング: 未完了/完了済み、カテゴリ別にタスクを絞り込み表示
  • 並び替え: 新しい順、古い順、優先度順にタスクを並び替え
  • 永続化: ブラウザを閉じてもデータが消えない(localStorageを使用)
  • テーマ切り替え: ダークモード/ライトモードを自由に切り替え可能
  • レスポンシブ対応: PC・スマホどちらでも快適に利用できるレイアウト

開発の背景:なぜTODOアプリ?

「TODOアプリなんて世の中にたくさんあるじゃん!」って思いましたか?その通りです。でも、だからこそ基礎固めに最適だと思います。

Web開発の学習を進める中で、ただ文法を覚えるだけでは「実際にアプリを作る」というイメージが湧きませんでした。そこで、普段使い慣れているTODOアプリを、自分で1から作ってみることにしました。

この開発を通して、機能追加、UI改善、コードのリファクタリングといった実際の開発現場に近いフローを体験できました。特に、コードが複雑になっていく中で「どう整理すればいいんだろう?」という壁にぶつかった経験は、今後の開発に活きる大きな学びでした。

技術選定:シンプルな構成でモダンなアプリ
今回選んだ技術スタックは、Web開発の基本中の基本。

言語 用途
HTML アプリの構造を作る
CSS 見た目をデザインする
JavaScript (ES Modules) アプリの動きと機能を実装する
Firebase Hosting 開発したWebアプリを公開する

特に、JavaScriptのES Modulesは、大規模なコードを機能ごとに分割して管理する上で非常に強力なツールです。最初は少し戸惑うかもしれませんが、必ず習得しておきたい技術なので、この記事でじっくり解説します。


1. アプリの基礎構築:まずは動かす!

まずは、TODOの追加と表示、削除といった最小限の機能から実装しました。

<div class="input-area">
    <input type="text" id="todo-input" placeholder="新しいTODOを入力">
    <button id="add-button">追加</button>
</div>
<ul id="todo-list"></ul>
// script.js (初期段階のイメージ): 基本的なDOM操作
const todoInput = document.getElementById('todo-input');
const addButton = document.getElementById('add-button');
const todoList = document.getElementById('todo-list');

addButton.addEventListener('click', () => {
    const todoText = todoInput.value.trim();
    if (todoText === '') return;

    const listItem = document.createElement('li');
    listItem.textContent = todoText;
    todoList.appendChild(listItem);
    todoInput.value = '';
});

学習のポイント:Webアプリの「骨組み・見た目・動き」の基本

このステップでは、Webアプリがどうやって動いているのか、その一番シンプルな形を学びました。

HTMLの役割
アプリの画面にどんな部品(入力欄、ボタン、リスト)を置くか、その骨組みを作るのがHTMLです。

JavaScriptの役割
ボタンを押したらどうなるか、文字を入力したらどうなるかなど、アプリに動きや機能を与えるのがJavaScriptです。特に、HTMLの部品(要素)をJavaScriptで操作するDOM操作という基本的なテクニックを身につけます。

CSSの役割
今はまだ紹介していませんが、CSSはこれらの部品の見た目(色、形、配置など)を整える役割があります。

どんなに複雑なWebアプリも、この「HTML」「CSS」「JavaScript」の組み合わせでできています。まずは、この3つの基本をしっかり押さえることが、Web開発の第一歩です!


2. アプリの機能拡張:TODOアプリを「使える」ものに!

単なる表示だけでなく、永続化/編集/フィルタリング/優先度/カテゴリ管理といった機能を追加していきました。

データの永続化 (localStorage)
せっかく入れたTODOが、ブラウザを閉じると消えちゃうのは困りますよね?それを防ぐために、ブラウザの中にデータを保存するlocalStorageという機能を使いました。

// utils.js (抜粋): localStorageの読み書き
export const getStoredTodos = () => {
    const storedTodos = JSON.parse(localStorage.getItem('todos')) || [];
    // 新しいプロパティが追加されても対応できるように、デフォルト値を設定
    return storedTodos.map(todo => ({
        id: todo.id,
        text: todo.text,
        completed: todo.completed,
        createdAt: todo.createdAt,
        category: todo.category || '未分類',
        priority: todo.priority || 'medium' // 優先度がない場合は'medium'
    }));
};

export const saveTodos = (todos) => {
    localStorage.setItem('todos', JSON.stringify(todos));
};

学習のポイント:データの「お引っ越し」と「互換性」

データの「お引っ越し」
localStorageは、Webアプリが使うデータを、パソコンやスマホのブラウザの中に「一時的に」保存しておく場所です。これを使えば、アプリを閉じたり、インターネットが繋がっていなくても、データが消えずに残るようになります。

「互換性」って何?
アプリに新しい機能を追加すると、昔入れたデータにその情報が残りません。getStoredTodosのコードにあるtodo.priority || 'medium'のように、古いデータでもちゃんとアプリが動くように、初期値(「medium」)を設定してあげる工夫が必要だと学びました。

TODOの編集機能

タスク名をダブルクリックすると編集できるようにしました。

// todoRenderer.js (抜粋): ダブルクリックで編集モードに
todoTextSpan.addEventListener('dblclick', async (event) => {
    event.stopPropagation(); // 親要素へのイベント伝播を防ぐ
    if (listItem.querySelector('.edit-input')) return; // 既に編集モードなら何もしない

    // inputフィールドとカテゴリ・優先度選択ドロップダウンを生成し、listItemに挿入
    const inputField = document.createElement('input');
    // ... (カテゴリ選択や優先度選択の生成) ...

    leftContent.style.display = 'none'; // 元の表示を隠す
    listItem.prepend(editPrioritySelect);
    listItem.prepend(editCategorySelect);
    listItem.prepend(inputField);
    inputField.focus();

    // 編集終了ロジック(外側クリック、Enterキー)
    // handleEditEnd関数で、更新処理を行いrenderTodosを呼び出し再描画
});

学習のポイント:ユーザーが「こうしたい」を叶えるテクニック

DOMの「着せ替え」:タスク名の表示(タグ)を、編集できる入力欄(タグ)に一時的に「着せ替え」るテクニックです。replaceChild()やprepend()といったJavaScriptの機能を使って、見た目を変えることができます。

カテゴリ管理機能(UIの進化)

当初はprompt()でカテゴリ名を削除していましたが、より使いやすいカテゴリ管理モーダルを導入しました。

// categoryManager.js (抜粋): カテゴリ管理モーダルを開く
export function openManageCategoriesModal(categoriesRef, todosRef, showCustomAlertFunc, updateCategorySelectsFunc, renderTodosFunc) {
    manageCategoriesModal.style.display = 'flex'; // モーダルを表示
    // モーダル内のカテゴリリストを動的にレンダリング
    renderCategoryListInModal(categoriesRef, todosRef, showCustomAlertFunc, updateCategorySelectsFunc, renderTodosFunc); 

    // モーダル内の「新しいカテゴリを追加」ボタンのイベントリスナー
    addCategoryModalButton.onclick = async () => {
        const newCat = await promptForNewCategory(categoriesRef, showCustomAlertFunc);
        if (newCat) {
            renderCategoryListInModal(categoriesRef, todosRef, showCustomAlertFunc, updateCategorySelectsFunc, renderTodosFunc);
        }
    };
}

学習のポイント:ユーザーの「もっとこうだったら」を形にする

UIの「かゆいところに手が届く」設計:単に機能があるだけでなく、それが「どう使われるか」を考えるのがUI/UXデザインです。ユーザーに負担をかけないように、プロンプト入力ではなく、一覧から選べるようなUIに改善しました。

データの「整合性」を保つ
カテゴリを削除する時、「そのカテゴリに紐づいていたTODOはどうなるの?」という問題が起こります。アプリとして矛盾がないように、関連するTODOのカテゴリを自動で 「未分類」 に変えるというルールを決めて実装しました。


3. UI/UXの改善:使って「気持ちいい」アプリへ!

アプリの印象はデザインと使いやすさで決まります。視覚的な改善と操作感の向上に取り組みました。

配色とタイポグラフィ、余白の最適化

コンテナの影、ボタンのホバーエフェクト、フォントサイズ・太さ、要素間の余白などを細かく調整し、統一感のあるモダンなデザインにしました。

/* _base.css (抜粋): 全体的なデザイン */
body {
    font-family: 'Inter', 'Arial', sans-serif;
    background-color: var(--bg-body); /* CSS変数でテーマ対応 */
    /* ... */
}
.container {
    box-shadow: 0 8px 25px var(--shadow-medium); /* 影で立体感 */
    /* ... */
}
#add-button:hover {
    transform: translateY(-2px); /* ホバーで少し浮き上がる */
}

学習のポイント:アプリの「第一印象」と「触り心地」

余白は「空気」
要素同士の間に適切な「隙間(余白)」を作ると、ごちゃごちゃせず、情報が整理されて見えます。これはデザインで最も基本的なことの一つで、見た目の美しさだけでなく、どこに何があるか分かりやすくする(視認性を上げる)ためにも欠かせません。

CSSアニメーションで「気持ちよさ」を演出
ボタンにマウスを乗せたり、クリックしたりしたときに、わずかに色が変わったり、動いたりする小さな変化(マイクロインタラクションと呼びます)は、ユーザーに「ちゃんと反応してくれた!」「気持ちいい!」という感覚を与えます。これは、アプリの品質を高く見せる魔法のようなテクニックです。

ダークモード/ライトモード切り替え

ユーザーの好みに合わせてテーマを切り替えられるようにしました。

/* _variables.css (抜粋): CSS変数でテーマ管理 */
:root { /* ライトモード */
    --bg-body: #eef4f7;
    --text-primary: #34495e;
    /* ... */
}
body.dark-mode { /* ダークモード */
    --bg-body: #2c3e50;
    --text-primary: #ecf0f1;
    /* ... */
}
// utils.js (抜粋): テーマ切り替えロジック
export const setPreferredTheme = (theme, themeToggleButton) => {
    localStorage.setItem('theme', theme);
    document.body.classList.toggle('dark-mode', theme === 'dark');
    themeToggleButton.textContent = theme === 'dark' ? '☀️' : '🌙'; // アイコンも切り替え
};

学習のポイント:CSS変数の「魔法の杖」

CSS変数って何?
CSS変数は、色の名前(--bg-bodyなど)に具体的な色(#eef4f7)を「代入」しておくような機能です。これを使うと、色を変えたいときに、その変数を定義している一箇所だけを書き換えれば、アプリ全体の同じ色が自動で変わります。

テーマ切り替えの仕組み
このCSS変数を「ライトモード用」と「ダークモード用」の2パターン用意しておき、JavaScriptでbodyタグにdark-modeというクラス(目印)を付けたり外したりするだけで、アプリ全体の色がパッと切り替わるようにしています。こ

レスポンシブデザイン(スマホ対応)

PCだけでなく、スマホでもレイアウトが崩れず、快適に使えるように調整しました。

/* _responsive.css (抜粋): スマホ向けスタイル */
@media (max-width: 768px) {
    .input-area {
        flex-direction: column; /* 横並びから縦並びに */
        gap: 12px;
    }
    #todo-input,
    .input-area select,
    #add-button {
        width: 100%; /* 幅を100%に */
        box-sizing: border-box;
    }
    #todo-list li {
        flex-direction: column; /* タスクアイテムも縦並びに */
        align-items: flex-start;
    }
    .todo-text {
        width: 100%; /* テキストも幅100% */
        font-size: 16px;
    }
    .todo-category, .todo-priority {
        margin-left: 0; /* 左寄せに調整 */
        margin-bottom: 5px; /* 縦方向の余白 */
    }
}

学習のポイント:ユーザーの「見る環境」に合わせた変化

mediaクエリの「条件分岐」
これはCSSの「もしもボックス」のようなもので、「もし画面の幅が〇〇px以下だったら、このスタイルを適用してね」という条件を設定できます。これを使うことで、デバイスの大きさに合わせて、レイアウト(要素の並び方)や文字の大きさをガラッと変えることができます。

Flexboxの「変身」能力
display: flex;を使った部品の並べ方(Flexbox)は、flex-direction: row;で横並び、flex-direction: column;で縦並び、flex-wrap: wrap;で自動改行といったように、とても柔軟に配置を変えることができます。小さな画面では、横に並びきらない要素を自動で縦に折り返すように設定することで、横スクロールを防ぎ、すべての情報がちゃんと見えるように工夫します。

box-sizing: border-box;の「縁の下の力持ち」
この設定は、パディング(要素の内側の余白)やボーダー(要素の枠線)を含めて要素の幅を計算してくれる魔法のようなプロパティです。これを使わないと、width: 100%にしてもパディングやボーダーのせいで画面からはみ出してしまうことがあるのですが、これを使えばぴったり収まるようになります。レスポンシブデザインでは、はみ出しを防ぐために非常に重要な設定です。


4. コードの整理:モジュール化で管理しやすいコードへ!

機能が増え、コードが数百行になると、一つのscript.jsファイルでは限界を感じました。そこで、コードを機能ごとに分割する「モジュール化」に挑戦しました。

my-todo-app/
├── index.html
├── style.css
├── src/
│ ├── main.js <-- アプリのメインエントリポイント
│ ├── utils.js <-- ユーティリティ関数
│ ├── domElements.js <-- DOM要素の取得
│ ├── categoryManager.js<-- カテゴリ管理関連
│ └── todoRenderer.js <-- TODOアイテム描画とリスト管理
│ └── css/ <-- CSSモジュール群
│ ├── _variables.css
│ ├── _base.css
│ ├── _forms.css
│ ├── _areas.css
│ ├── _todo-list.css
│ ├── _modals.css
│ └── _responsive.css

// main.js (抜粋): 各モジュールをインポートし連携させる
import * as DOM from './domElements.js';
import { getStoredTodos, saveTodos } from './utils.js';
import { renderTodos } from './todoRenderer.js';

// ... (状態変数とメインロジック) ...

// DOMContentLoaded イベント後にアプリを初期化
document.addEventListener('DOMContentLoaded', () => {
    setupCustomAlert();
    initializeApp();
});

学習のポイント:コードの「整理整頓」と「部品化」

コードの「整理整頓
モジュール化とは、大きなコードを機能ごとに「ファイル」という引き出しに分けて整理するようなものです。例えば、データ保存のコードはutils.js、カテゴリのコードはcategoryManager.jsのように、役割ごとにファイルを分けることで、どこに何が書いてあるか一目でわかるようになります。これにより、後から機能を追加したり、バグを直したりする際に、探す手間が格段に減り、効率が上がります。

「部品」として使うexportimport
それぞれのファイルで、他のファイルに使ってほしい機能にはexport(「外に出すよ!」)という目印をつけます。そして、その機能を使いたいファイルではimport(「使うよ!」)という命令でその機能を取り込みます。これにより、まるでレゴブロックのように、必要な部品だけを組み合わせてアプリを作ることができるようになります。


5. Firebase Hostingでの公開

開発したアプリを、無料で簡単にWeb上に公開できる Firebase Hosting を使ってデプロイしました。

# Firebase CLIをインストール
npm install -g firebase-tools

# Firebaseにログイン
firebase login

# プロジェクトの初期化 (公開ディレクトリは '.' に設定)
firebase init

# デプロイ
firebase deploy --only hosting

学習のポイント:自分の作品を世界に発信する!

アプリ公開の「ハードル」を下げる
Firebase Hostingのようなサービスを使えば、専門的なサーバーの知識がなくても、数分で自分の作ったWebアプリをインターネット上に公開できます。これは、自分の作品をポートフォリオとして見せたり、友達に共有したりする上で、非常に大きなメリットです。

Web開発の「ゴール」が見える
コードを書くだけでなく、実際にアプリがウェブ上で動いているのを見ると、「自分はWeb開発ができるんだ!」という大きな達成感と自信に繋がります。この成功体験が、次の学習へのモチベーションになります。

学んだこと・得られた知見

このTODOアプリ開発を通して、以下のスキルと知見を得ることができました。

  • Web開発の総合的な実践力
    ただコードを書けるだけでなく、「アプリとして動かし、ユーザーに使ってもらう」ための知識と技術。

  • UI/UXデザインの重要性
    見た目の美しさだけでなく、ユーザーが迷わず、快適に使えるかがアプリの価値を決めると学びました。

  • コードの保守性・拡張性
    コードが成長しても、後から機能を追加したり、修正したりしやすいように、整理整頓されたコードを書くことの重要性を学びました。

  • Webアプリの公開経験
    自分の作ったものが世界中で見られるという貴重な経験を積めました。

おわりに

長くなりましたが、最後まで読んでいただきありがとうございます!

Webアプリ開発は、最初は複雑に感じるかもしれませんが、一つ一つの機能を着実に実装し、UI/UXを改善し、コードを整理していく過程は、まるでパズルを組み立てるようでとても楽しいです。

この記事が、あなたのWeb開発学習の助けになれば幸いです。ぜひ、あなたも自分だけのオリジナルWebアプリ開発に挑戦してみてください!

8
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?