10-1. 要件を読み、UIの土台を作る
このセクションで学ぶこと
Section titled “このセクションで学ぶこと”- 書籍管理アプリに必要な画面要素を HTML で整理する方法
header/main/section/form/tableなどの意味のあるタグの使い分け- CSS でフォーム・一覧・状態バッジを読みやすく整える方法
- JavaScript を書く前に、なぜ静的な UI を先に作るのか
- Elements / Styles パネルで画面崩れを自力で確認する基本
第10章では、1つの書籍管理アプリをフロントエンドからバックエンドまで少しずつ育てていく。
最初のこのセクションでは、まだボタンは動かさず、「どこに何を表示するのか」 がはっきり分かる静的な画面を作る。
このセクションの終わりでは、次のような静的画面ができている状態を目指す。
- ページの目的が分かるヘッダー
- 書籍を登録するためのフォーム
- 登録済み書籍を確認する一覧テーブル
- 「未読 / 読書中 / 読了」が見分けやすい状態バッジ
- 次のセクションで JavaScript をつなぎやすい、整理された HTML 構造
+----------------------------------------------------------------+| 書籍管理アプリ || いま読んでいる本と、これから読む本を整理する || [登録冊数 3] [読書中 1] [読了 1] ||----------------------------------------------------------------|| 書籍を登録する || タイトル [________________] 著者 [________________] || カテゴリ [select] 価格 [____] || 状態 [select] || メモ [______________________________________________] || [書籍を追加] ||----------------------------------------------------------------|| 登録済み書籍 || JavaScript入門 | 山田太郎 | フロントエンド | 2800円 | 未読 | 削除 || Git実践入門 | 佐藤花子 | 開発ツール | 2400円 | 読了 | 削除 |+----------------------------------------------------------------+1. まず画面の役割を分解する
Section titled “1. まず画面の役割を分解する”JavaScript を先に書きたくなるかもしれないが、初心者ほど 画面の骨組みを先に固定する ほうが安全である。
理由は 3 つある。
- どのデータを扱う画面なのかが明確になる
- JavaScript から取得したい要素に名前や役割を付けやすくなる
- 見た目の崩れとロジックのバグを分けて考えられる
HTML = 何の部品があるかCSS = どう見せるかJS = いつ、どう動くかこの順で考えると、
- 画面がないのか
- 画面はあるが崩れているのか
- 画面は正しいが JavaScript が動かないのか
を切り分けやすくなる。
今回の書籍管理アプリでは、まず次の 3 領域を作る。
| 領域 | 役割 | このあと何に使うか |
|---|---|---|
| ヘッダー | 何のアプリかを伝える | 件数表示をあとで JS から更新する |
| 登録フォーム | 新しい本の入力欄 | submit イベントをあとで受け取る |
| 一覧テーブル | 登録済みの本を表示する | DOM 操作で行を追加・削除する |
2. ファイル構成を確認する
Section titled “2. ファイル構成を確認する”10-0 で book-app/book-frontend/ フォルダは作成済みである。
このセクションでは book-frontend/ の中に 2 ファイルを追加していく。
book-app/├── book-frontend/ ← このフォルダを VS Code で開く│ ├── index.html ← 画面の部品を書く│ └── style.css ← 見た目のルールを書く└── bookmanager/VS Code で book-app/book-frontend/ フォルダを開いてから作業を始める(File > Open Folder...)。
「HTML に見た目も全部書く」こともできるが、役割を分けたほうが後から修正しやすい。
3. HTML の土台を作る
Section titled “3. HTML の土台を作る”まずはページ全体の骨組みを作る。
<!doctype html><html lang="ja"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>書籍管理アプリ</title> <link rel="stylesheet" href="./style.css" /> </head> <body> <main class="app-shell"> <header class="page-header"> <p class="page-kicker">Chapter 10 Book App</p> <h1>書籍管理アプリ</h1> <p class="page-description"> いま読んでいる本、次に読みたい本、読み終えた本を整理するための練習用アプリです。 </p> </header>
<section class="summary-grid" aria-label="現在の集計"> <article class="summary-card"> <p class="summary-label">登録冊数</p> <p class="summary-value">3</p> </article> <article class="summary-card"> <p class="summary-label">読書中</p> <p class="summary-value">1</p> </article> <article class="summary-card"> <p class="summary-label">読了</p> <p class="summary-value">1</p> </article> </section> </main> </body></html>なぜ main や header を使うのか
Section titled “なぜ main や header を使うのか”div だけでも画面は作れる。
しかし、意味のあるタグを使うと、人が見ても、ブラウザが見ても構造が分かりやすい。
main = このページの主役の内容header = タイトルと導入section = まとまりごとの領域この「意味」があると、あとで JavaScript で要素を探すときにも頭の中が整理しやすい。
4. HTTP サーバーを起動してブラウザで確認する
Section titled “4. HTTP サーバーを起動してブラウザで確認する”index.html ができたら、すぐにブラウザで確認できる状態にしておこう。
10-5 以降では fetch() で API を呼び出すため、最初から HTTP サーバー経由で開く習慣にしておくと、各ステップの変更をそのままブラウザで確認し続けられる。
VS Code のターミナルを新しく開き(Ctrl + @)、book-app/book-frontend/ にいることを確認してから次を実行する。
npx --yes serve . --listen 4173コマンドを実行したままにして、ブラウザで http://localhost:4173/ を開く。
確認が終わったら、そのターミナルで Ctrl + C を押して停止する。
このターミナルは開いたままにしておく 以降のセクションで HTML や CSS を更新したあと、ブラウザで
F5(またはCtrl+R)を押すだけで最新の表示を確認できる。 ターミナルを閉じると停止した場合は、次回作業を再開するときに同じコマンドを再実行する。
この時点で確認できること
Section titled “この時点で確認できること”style.css はまだ存在しないので見た目は整っていないが、次のことは確認できる。
書籍管理アプリという見出しが表示される- 集計カードの「登録冊数・読書中・読了」のラベルが見える
- フォームと一覧テーブルは次のセクション以降で追加する
5. 書籍登録フォームを作る
Section titled “5. 書籍登録フォームを作る”次に、入力欄を追加する。 書籍管理アプリでは、少なくとも次の情報を扱えるようにしておくと練習しやすい。
- タイトル
- 著者
- カテゴリ
- 価格
- 状態
- メモ
追加する場所: セクション3で書いた <section class="summary-grid"> の閉じタグ </section> の直後、</main> の手前。
</section><!-- summary-grid の閉じタグ(すでにある) -->
<section class="panel"> <h2>書籍を登録する</h2>
<form class="book-form"> <div class="form-grid"> <label class="form-field" for="title"> <span>タイトル</span> <input id="title" name="title" type="text" placeholder="例: JavaScript入門" /> </label>
<label class="form-field" for="author"> <span>著者</span> <input id="author" name="author" type="text" placeholder="例: 山田太郎" /> </label>
<label class="form-field" for="category"> <span>カテゴリ</span> <select id="category" name="category"> <option value="">選択してください</option> <option value="フロントエンド">フロントエンド</option> <option value="バックエンド">バックエンド</option> <option value="データベース">データベース</option> <option value="開発ツール">開発ツール</option> </select> </label>
<label class="form-field" for="price"> <span>価格</span> <input id="price" name="price" type="number" min="0" step="1" placeholder="2800" /> </label>
<label class="form-field" for="status"> <span>状態</span> <select id="status" name="status"> <option value="未読">未読</option> <option value="読書中">読書中</option> <option value="読了">読了</option> </select> </label> </div>
<label class="form-field" for="memo"> <span>メモ</span> <textarea id="memo" name="memo" rows="4" placeholder="この本で何を学びたいかを書いておく"></textarea> </label>
<button class="primary-button" type="submit">書籍を追加</button> </form> </section>
</main><!-- main の閉じタグ(すでにある) -->なぜ label と for が重要なのか
Section titled “なぜ label と for が重要なのか”見た目だけなら、
<p>タイトル</p><input type="text" />でも表示はできる。
しかし label for="title" と input id="title" を対応させておくと、
- どの入力欄の説明かが明確になる
- ラベルをクリックすると入力欄にフォーカスできる
- JavaScript から
#titleとして取り出しやすい
という利点がある。
つまり label は飾りではなく、入力欄の意味を固定するための部品 である。
ブラウザで確認する
Section titled “ブラウザで確認する”index.html を保存してブラウザで F5 を押す。
- タイトル・著者・カテゴリ・価格・状態・メモの入力欄が表示されている
- 「書籍を追加」ボタンが見える
- まだ CSS が当たっていないためスタイルは整っていないが、構造が確認できれば次へ進んでよい
6. 一覧テーブルを作る
Section titled “6. 一覧テーブルを作る”フォームの次は、登録済み書籍を表示する一覧を作る。 最初は静的な行を 2〜3 件入れて、画面の形だけ確認すればよい。
追加する場所: セクション5で追加したフォームの </section> の直後、</main> の手前。
</section><!-- フォームの閉じタグ(セクション4で追加済み) -->
<section class="panel"> <div class="section-heading"> <h2>登録済み書籍</h2> <p class="section-note">この段階ではまだサンプル表示です</p> </div>
<div class="table-wrapper"> <table class="book-table"> <thead> <tr> <th>タイトル</th> <th>著者</th> <th>カテゴリ</th> <th>価格</th> <th>状態</th> <th>メモ</th> <th>操作</th> </tr> </thead> <tbody id="book-table-body"> <tr> <td>JavaScript入門</td> <td>山田太郎</td> <td>フロントエンド</td> <td>2800円</td> <td><span class="status-badge status-unread">未読</span></td> <td>DOM 操作の章まで読む</td> <td><button class="ghost-button" type="button">削除</button></td> </tr> <tr> <td>Git実践入門</td> <td>佐藤花子</td> <td>開発ツール</td> <td>2400円</td> <td><span class="status-badge status-finished">読了</span></td> <td>ブランチ運用を復習済み</td> <td><button class="ghost-button" type="button">削除</button></td> </tr> </tbody> </table> </div> </section>
</main><!-- main の閉じタグ(すでにある) -->なぜ最初から tbody を分けるのか
Section titled “なぜ最初から tbody を分けるのか”次のセクションでは JavaScript で行を追加・削除する。
そのとき、操作対象を tbody にまとめておくと、
thead = 見出し(固定)tbody = データ部分(あとで動的に書き換える)という役割分担ができる。
つまり tbody を作っておくことは、あとで DOM を更新しやすくする準備 である。
ブラウザで確認する
Section titled “ブラウザで確認する”index.html を保存してブラウザで F5 を押す。
- テーブルの見出し行(タイトル・著者・カテゴリ・価格・状態・メモ・操作)が表示されている
- サンプル 2 行(JavaScript入門・Git実践入門)が表示されている
- 「削除」ボタンが各行に見える
- CSS は次のセクションで追加するためまだ見た目は整っていないが、行が並んでいれば次へ進んでよい
7. CSS でレイアウトを整える
Section titled “7. CSS でレイアウトを整える”HTML だけでも情報は並ぶが、そのままだとフォームも表も読みづらい。
ここでは、余白・色・並び方を整えて「読み間違いにくい UI」を作る。
6-1. ページ全体の土台
Section titled “6-1. ページ全体の土台”:root { --bg: #f5f7fb; --panel: #ffffff; --line: #d7dce5; --text: #1f2937; --subtle: #667085; --primary: #2563eb; --primary-soft: #dbeafe;}
* { box-sizing: border-box;}
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: linear-gradient(180deg, #eef4ff 0%, var(--bg) 240px); color: var(--text);}
.app-shell { width: min(1100px, calc(100% - 32px)); margin: 0 auto; padding: 48px 0 80px;}
.page-header { margin-bottom: 24px;}
.page-kicker { margin: 0 0 8px; color: var(--primary); font-size: 0.85rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;}
.page-header h1 { margin: 0; font-size: clamp(2rem, 3vw, 2.8rem);}
.page-description { margin: 12px 0 0; color: var(--subtle); line-height: 1.8;}ブラウザで確認する
Section titled “ブラウザで確認する”style.css を保存してブラウザで F5 を押す。
- ページの背景がうっすら青みがかった色に変わっている
- コンテンツが画面中央に収まり、左右に余白がついている
6-2. 集計カードとパネル
Section titled “6-2. 集計カードとパネル”追加する場所: style.css の末尾に続けて追記する。
.summary-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; margin-bottom: 24px;}
.summary-card,.panel { background: var(--panel); border: 1px solid var(--line); border-radius: 16px; box-shadow: 0 12px 30px rgba(15, 23, 42, 0.06);}
.summary-card { padding: 20px;}
.summary-label { margin: 0; color: var(--subtle); font-size: 0.9rem;}
.summary-value { margin: 8px 0 0; font-size: 2rem; font-weight: 700;}
.panel { padding: 24px; margin-bottom: 20px;}
.section-heading { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 16px;}
.section-note { margin: 0; color: var(--subtle); font-size: 0.9rem;}ブラウザで確認する
Section titled “ブラウザで確認する”style.css を保存してブラウザで F5 を押す。
- 集計カード 3 枚が横並びになっている
- フォームと一覧が白い枠付きのパネルで囲まれている
6-3. フォームと一覧テーブル
Section titled “6-3. フォームと一覧テーブル”追加する場所: style.css の末尾に続けて追記する。
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px;}
.form-field { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px;}
.form-field span { font-weight: 600;}
.form-field input,.form-field select,.form-field textarea { width: 100%; padding: 12px 14px; border: 1px solid var(--line); border-radius: 12px; font: inherit; background: #fff;}
.primary-button,.ghost-button { border-radius: 999px; padding: 10px 18px; font: inherit; cursor: pointer;}
.primary-button { border: none; background: var(--primary); color: #fff;}
.ghost-button { border: 1px solid var(--line); background: #fff; color: var(--text);}
.table-wrapper { overflow-x: auto;}
.book-table { width: 100%; border-collapse: collapse;}
.book-table th,.book-table td { padding: 14px 12px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top;}
.book-table th { color: var(--subtle); font-size: 0.9rem;}
.status-badge { display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 999px; font-size: 0.82rem; font-weight: 700;}
.status-unread { background: #e5e7eb; color: #374151;}
.status-reading { background: #fef3c7; color: #92400e;}
.status-finished { background: #dcfce7; color: #166534;}
@media (max-width: 720px) { .summary-grid, .form-grid { grid-template-columns: 1fr; }}CSS で大事なのは「映えること」より「読み間違えないこと」
Section titled “CSS で大事なのは「映えること」より「読み間違えないこと」”初心者は色や装飾に意識が向きやすいが、実務でより重要なのは次の点である。
- 入力欄がどこか一目で分かる
- ボタンが押せそうに見える
- 状態の違いが色と文字の両方で分かる
- スマホ幅でも崩れにくい
つまり CSS は飾りではなく、利用者が迷わないための設計 でもある。
ブラウザで確認する
Section titled “ブラウザで確認する”style.css を保存してブラウザで F5 を押す。
- フォームの入力欄が 2 列で並んでいる
- 「書籍を追加」ボタンが青い丸ボタンになっている
- テーブルの行に罫線が入っている
- 「未読」バッジがグレー、「読了」バッジが緑で表示されている
8. ここまでを 1 画面として見る
Section titled “8. ここまでを 1 画面として見る”この時点ではまだデータは固定だが、すでに次の準備ができている。
[フォーム] title / author / category / price / status / memo ↓ まだ送信はしない ↓[一覧テーブル] あとで JavaScript が tbody に行を追加する ↓[集計カード] あとで JavaScript が数字を書き換える画面の置き場所が決まっていると、次のセクションでは
- フォームの値を読む
- 一覧に新しい行を出す
- 件数を更新する
という処理に集中できる。
よくある失敗
Section titled “よくある失敗”| 失敗 | 起きること | 直し方 |
|---|---|---|
label for と input id が一致していない | ラベルをクリックしても入力欄へ移動しない | for="title" と id="title" のように同じ名前へそろえる |
display: grid を .form-grid ではなく各 label に書いている | 期待した列配置にならない | 「複数の項目を並べたい親要素」に grid を指定する |
tbody を作らず、行を直接 table の下に書いている | あとで JavaScript でどこを書き換えるか分かりにくい | thead と tbody を分ける |
| 状態バッジのクラス名を打ち間違えている | 色が変わらず、見分けにくい | status-unread / status-reading / status-finished を確認する |
ボタンに type="button" を付けていない | 後でフォームの中に置いたとき、意図せず submit されることがある | 送信用は submit、その他は button と明示する |
HTML/CSS の不具合は、勘で直すより DevTools で事実を見る ほうが早い。
1. Elements パネルで構造を見る
Section titled “1. Elements パネルで構造を見る”確認したい点:
main.app-shellがあるかform.book-formの中に#titleや#authorがあるかtable > thead + tbodyの形になっているか- 状態バッジに期待したクラスが付いているか
Elements└─ main.app-shell ├─ header.page-header ├─ section.summary-grid ├─ section.panel (form) └─ section.panel (table)2. Styles / Computed で CSS の効き方を見る
Section titled “2. Styles / Computed で CSS の効き方を見る”- 期待した CSS が打ち消されていないか
display: gridやpaddingが本当に適用されているかwidth: 100%が効いているか
特に「見た目が崩れた」ときは、要素にクラスが付いていないのか、クラスはあるが CSS が当たっていないのか を分けて考える。
3. 画面幅を変えて確認する
Section titled “3. 画面幅を変えて確認する”Chrome のデバイスツールバーで幅を狭め、
- 集計カードが縦に並ぶか
- フォームが 1 列になるか
- テーブルが横スクロールできるか
を確認すると、あとから大きく直しにくい崩れを早めに見つけられる。
| 項目 | ポイント |
|---|---|
| HTML の骨組み | main / header / section で役割を分ける |
| フォーム | label と id を対応させて入力欄の意味を固定する |
| 一覧表示 | thead と tbody を分けると次の DOM 操作が楽になる |
| CSS の役割 | 余白・配置・状態の違いを整理して読みやすくする |
| 状態バッジ | 色だけでなく文字でも状態を伝える |
| デバッグ | Elements / Styles で「構造」と「適用中の CSS」を切り分ける |
次のステップ
Section titled “次のステップ”演習問題 で、静的な UI を自分の手で組み立てられるか確認しよう。
できたら 10-2. フロントエンドで一覧表示とフォーム入力を作る へ進み、同じ画面に動きを付けていこう。