Old-School Front-End / 2026 ・ No.02

いにしえのフロントエンド入門

第2回 HTML の骨組み ── 申請一覧を「意味のあるタグ」で組み立てる

公開

CSS を付けていない、HTML だけの申請一覧のページ。ロゴと「経費精算システム」のリンク、箇条書きのメニュー(申請一覧・承認・ダッシュボード)、利用者名、大見出し「申請一覧」、新規申請のリンク、今月のまとめの箇条書き、2026年10月の申請の表、著作権表示が縦に並ぶ
今回のゴール。HTML だけで組んだ申請一覧です。飾りはまだありませんが、中身の「骨」はすべてそろっています(筆者のデモページを、クラウド上の Chromium で撮影。2026年10月17日。ブラウザの枠は説明のために描き足したもの)

AI に頼まず、一行ずつ手で書く「いにしえの」フロントエンド入門。第2回から、いよいよ工事が始まります。
今回作るのは、経費精算システムの申請一覧の骨組み。CSS も JavaScript も使わず、HTML だけで組み立てます。
キーワードは「意味のあるタグ」。見た目が同じでも、タグの選び方ひとつで、読み上げソフトにも、テストの道具にも、AI にも、
ページの中身の伝わり方がまるで変わります。最後は、書いた HTML を検査ツールにかけるところまで進みます。

一

今回のゴール

新井先輩、申請一覧の画面、AI に作ってもらいました! ほら、ちゃんと表示されます。

古井どれどれ……div、div、div、div。見事な「div スープ」だな。

新井でも、見た目はちゃんとしてますよ?

古井見た目はな。今日は、見た目の下にある「骨」の話だ。

いまここ:第2回

1 準備 2 HTML の骨組み 3 フォーム 4 CSS の基本 5 レイアウト 6 Bootstrap 7 JS の基本 8 DOM・イベント 9 入力チェック 10 通信 11 ログイン 12 承認の流れ 13 集計と印刷 14 jQuery 15 仕上げ いまここ 準備 HTML CSS JavaScript 実戦 仕上げ

今回は、前回「建設予定地」だった index.html を、申請一覧のページに建て替えます。中身は次のとおりです。

  • ページの頭 ── ロゴとサイト名、メニュー(申請一覧・承認・ダッシュボード)、ログイン中の利用者
  • ページの本体 ── 大見出し、新規申請へのリンク、今月のまとめ、申請の一覧表
  • ページの足 ── 著作権の表示

CSS はまだ使わないので、見た目は素朴なままです(服を着せるのは第4回)。そのぶん、どのタグを選ぶかに集中できます。

二

タグ・要素・属性

まずは部品の名前から。HTML は、タグで文字を挟んで「これは何か」を示す言語です。

タグの解剖図

<a href="form.html"> + 新規申請 </a> 属性 名前="値" 開始タグ 中身 終了タグ ここまで全部で、一つの「要素」 <img src="logo.svg" alt=""> 空要素 中身も終了タグもない img・br・input・meta タグ名は小文字で書くのがお約束。属性の値は " " で囲む
  • 要素 ── 開始タグ・中身・終了タグをまとめたもの。「a 要素」「table 要素」のように呼ぶ
  • 属性 ── 要素に付ける追加の情報。開始タグの中に 名前="値" の形で書く
  • 空要素 ── 中身を持たない要素。終了タグも書かない(img、br、input、meta など)

どの要素にも付けられる属性のうち、よく使うのはこの三つです。

属性意味例
idページの中で一つだけの名前。同じ id を2回使ってはいけない<table id="list">
classグループの名前。何個に付けてもよい。CSS(第4回)や JavaScript(第8回)で狙い撃ちするときに使う<td class="amount">
lang中身の言語。ふつうは html 要素に一度だけ<html lang="ja">

もう一つ大事なのが、入れ子のきまりです。要素の中に要素を入れるときは、箱の中に箱を入れるように、後から開いたタグを先に閉じます。

入れ子のきまり

○ 箱の中に箱 p strong a リンクの文字 <p><strong><a>…</a></strong></p> 後から開いたタグを、先に閉じる × 箱が交差 strong a <strong><a>…</strong></a> strong を閉じたのに a が開いたまま マトリョーシカの原則:開けた順の逆に閉じる。交差するとブラウザが勝手に組み直す

前回、</h1> を消したら全部が見出しになったのを覚えているでしょうか。ブラウザは間違いがあってもエラーを出さず、自分なりに木を組み直して表示します。その「自分なり」が、こちらの意図と合っている保証はありません。

三

ページの区画(ランドマーク)

ページはふつう、頭(ヘッダー)・メニュー・本体・足(フッター)に分かれます。HTML には、それぞれ専用のタグがあります。こうした区画をランドマーク(目印)と呼びます。

div スープと、意味のあるタグ

div スープ(何が何だか) <div> <div> <div> <div> <div> <div> <div> <div> <div> <div> <div> <div> <div> 意味のあるタグ(役割が伝わる) <header> banner <main> main <footer> contentinfo <nav> navigation <h1> <section> <h2> <ul> <section> <h2> <table> 右側の小さな英語は、読み上げソフトなどに伝わる「役割(ロール)」。div には役割がない
タグ意味この連載での使いどころ
headerページ(または区画)の頭ロゴ・サイト名・利用者名
nav主なナビゲーション(メニュー)申請一覧・承認・ダッシュボードへのリンク
mainそのページの主役。1ページに1つ見出し・まとめ・一覧表
section見出しを持つ、ひとまとまりの内容「今月のまとめ」「申請の一覧」
articleそれだけで独立して意味を持つ内容(ブログの記事、ニュースの1件など)
aside本筋の脇にある補足(第13回のダッシュボードで使うかも)
footerページ(または区画)の足著作権表示・問い合わせ先
div意味のない箱。見た目のためだけにまとめたいときほかに合うタグがないときの最後の手段

では、div だけで作ったページと、意味のあるタグで作ったページを比べてみましょう。

二つのページを左右に並べた画面。1 div だけで作ったページと、2 意味のあるタグで作ったページ。見出しや箇条書き、リンクの見た目はほぼ同じ
①は div と見た目の指定だけで「それっぽく」作ったページ、②は意味のあるタグで作ったページ。画面で見る限り、ほとんど区別がつきません(筆者のデモページを、クラウド上の Chromium で撮影。2026年10月17日。ブラウザの枠は説明のために描き足したもの)

画面で見る限り、ほとんど同じです。ところが、読み上げソフトなどの支援技術には、まるで違って見えています。ブラウザがページから作る「アクセシビリティの木」を、テストの道具 Playwright で取り出してみました(実際の出力。長い部分は省略)。

# ① div スープ:ほぼ「画像1枚と、ひと続きの文字」
- img
- text: 経費精算システム • 申請一覧 • 承認 • ダッシュボード 新井 太郎(申請者) 申請一覧 + 新 …

# ② 意味のあるタグ:区画・見出し・リスト・表がそのまま伝わる
- banner:
  - paragraph:
    - link "経費精算システム":
      - /url: index.html
  - navigation:
    - list:
      - listitem:
        - link "申請一覧":
          - /url: index.html
      - listitem:
        - link "承認":
          - /url: approve.html
      - listitem:
        - link "ダッシュボード":
          - /url: dashboard.html
  - paragraph: 新井 太郎(申請者)
- main:
  - heading "申請一覧" [level=1]
  - paragraph:
    - link "+ 新規申請":
      - /url: form.html
  - heading "今月のまとめ" [level=2]
  - list:
    - listitem: 今月の申請:4 件
    - listitem:
      - text: 承認待ちの金額:
      - strong: 32,400 円
    - listitem: 差し戻し:1 件
  - heading "申請の一覧" [level=2]
  - table "2026年10月の申請(新しい順)":
    …

②では、banner(header)、navigation(nav)、main、見出しのレベル、リストの数、表の見出しまでが伝わっています。①では、リンクの役割すら消えてしまいました(span をクリックで動くようにしただけなので、キーボードの Tab キーでもたどり着けません)。

  • 読み上げソフトの利用者 ── 見出しを飛び移ってページの全体をつかむ人が多い(WebAIM の調査)。ランドマークも同じように「目次」として使える。div スープでは、頭から全部聞くしかない
  • テストの道具 ── 開発ガイドのPlaywright 入門と実践で紹介した getByRole('navigation') のような探し方は、意味のあるタグがあって初めて使える
  • AI のツール ── ブラウザを操作する AI も、こうした役割の情報を手がかりに画面を読む。人にとって分かりやすいページは、機械にとっても分かりやすい

古井div は「名無しの箱」だ。中に何が入っているか、外からは誰にも分からん。

新井AI に頼むときも「意味のあるタグで」って一言足せばいいんですね。……で、出てきたのが正しいかは、自分で読んで確かめる、と。

古井分かってきたじゃないか。

四

DOM の木

第1回で、ブラウザは HTML を読んで DOM という木を作る、と書きました。今回のページを木にすると、こうなります。

申請一覧のページの DOM(主な部分)

html head 画面に出ない情報 meta, title 文字コード・題名 body 画面に出る中身 header ページの頭(サイト名・メニュー・利用者) p, nav, p サイト名 / メニュー / 利用者名 main このページの主役(1ページに1つ) h1 大見出し「申請一覧」 p > a 新規申請へのリンク section 今月のまとめ(h2 と ul) section 申請の一覧(h2 と table) table 申請の表 footer ページの足(著作権表示)

インデント(字下げ)をそろえて HTML を書くと、コードの形がそのまま木の形になります。字下げは見た目の飾りではなく、木の形を目で確かめるための道具です。ブラウザで要素を右クリックして「検証」を選ぶと、開発者ツールの Elements タブで、この木を実物で見られます。

POINT

あとで CSS は「木のどの枝に服を着せるか」、JavaScript は「木のどの枝を動かすか」を指定します。きれいな木を作っておくことが、後半を楽にする一番の近道です。

五

見出し・段落・リスト

見出しには h1 から h6 までの6段があります。大事なのは、大きさではなく、文章の構造で選ぶこと。

見出しは目次になる

○ 段を飛ばさない h1 申請一覧 h2 今月のまとめ h2 申請の一覧 × 見た目で選ぶ h1 申請一覧 h4 今月のまとめ h4 申請の一覧 ・見出しの段(h1〜h6)は、文章の構造で選ぶ。大きさは CSS で変えられる(第4回) ・読み上げソフトの利用者は、見出しを飛び移ってページの中身をつかむことが多い ・h1 はページの題名にあたる1つだけにするのが分かりやすい

かつて HTML の仕様には、section の入れ子から見出しの段を自動で決める仕組み(アウトライン・アルゴリズム)が書かれていましたが、どのブラウザも実装しないまま、2022年に仕様から削除されました。見出しの段は、自分で正しく付けるのが今のきまりです。

タグ意味申請一覧では
h1〜h6見出し(段の数字が小さいほど大きなまとまり)h1「申請一覧」、h2「今月のまとめ」「申請の一覧」
p段落サイト名、利用者名、新規申請のリンク
ul / ol / li順番のないリスト/順番のあるリスト/その1項目メニュー、今月のまとめ
strong重要(強く読む)承認待ちの金額
em強調(言い方を変える)(今回は使わない)
small注記・著作権などの小さな文字フッターの著作権表示
time日付や時刻。datetime 属性に機械が読める形で書く申請日

メニューを ul で書くのは、「項目が3つある、ひとまとまりのリスト」だからです。読み上げソフトは「リスト、3項目」と先に教えてくれます。<br> で改行を重ねて並べると、この情報は伝わりません。

六

表を組む

申請の一覧は、行(1件の申請)と列(申請日・件名・金額・状態)のあるデータです。こういうデータには table を使います。

表の部品

2026年10月の申請(caption) No. 申請日 件名 金額 0007 10-16 大阪出張 32,400 0006 10-15 電車代 3,830 0005 10-09 技術書 4,180 caption thead tbody th tr td ・table = 表全体、tr = 1行、th = 見出しのマス、td = データのマス ・thead と tbody で「見出しの行」と「中身の行」を分ける。caption は表の題名 ・th の scope="col" は「この列の見出し」の意味。読み上げで各マスの見出しが伝わる ・表は、行と列のあるデータのためのもの。画面の配置に使うのは禁じ手
<table>
  <caption>2026年10月の申請(新しい順)</caption>
  <thead>
    <tr>
      <th scope="col">No.</th>
      <th scope="col">申請日</th>
      <!-- …件名・金額(円)・状態… -->
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0007</td>
      <td><time datetime="2026-10-16">2026-10-16</time></td>
      <!-- …1行に、見出しと同じ数のマス… -->
    </tr>
  </tbody>
</table>

古井昔はな、画面の配置を全部 table で組んだもんだ。隙間を作るのに、透明な小さい画像を敷き詰めてな。

新井それはさすがに、いにしえすぎます。

古井だから言うんだ。表は表のためだけに使え。配置は CSS の仕事だ(第5回)。

  • 状態は文字で書く ── あとで「申請中は黄色、差し戻しは赤」と色を付けても、色だけに頼らず文字を残す。色の見分けにくい人や、白黒で印刷したときにも伝わる
  • 金額の列 ── 「金額(円)」と見出しに単位を書けば、各マスは数字だけで済む。右寄せにするのは CSS の仕事(第4回)
  • 行の数は決め打ちしない ── 今は手で4行書いているが、第10回からはデータを読み込んで JavaScript で行を作る
七

リンクと画像

リンクは a 要素の href、画像は img 要素の src で、行き先(ファイルの場所)を書きます。書き方は三種類あります。

リンク先の書き方

keihi/ index.html form.html img/ logo.svg help/ faq.html ①② は index.html、③ は faq.html から 1 href="form.html" 同じフォルダなら、名前だけ 2 src="img/logo.svg" 下のフォルダへは「フォルダ名/」 3 href="../index.html" 「../」は一つ上のフォルダ ・ほかのサイトへは https:// から全部書く(絶対 URL) ・同じページの中の場所へは #id 例:href="#list" で id="list" の要素へ飛ぶ index.html を基準にした「相対パス」で書いておくと、フォルダごと引っ越しても壊れない
書き方例使いどころ
相対パスform.html、img/logo.svg、../index.html自分のサイトの中。今いるファイルからの道順
絶対 URLhttps://getbootstrap.com/ほかのサイト
ページ内リンク#list同じページの id="list" の場所へ

リンクを新しいタブで開く target="_blank" には、昔は安全のために rel="noopener" を必ず添えましたが、今の主要なブラウザはこれを自動で付けてくれます。ただ、業務システムで勝手にタブが増えるのは嫌がられがち。新しいタブは、外部のサイトや PDF を開くときくらいにしておきましょう。

画像で忘れてはいけないのが alt(代わりの文字)です。画像が見えない人や、画像が読み込めなかったときに、代わりに伝わる文章です。

画像の種類alt の書き方例
意味を伝える画像画像が伝えている内容を書くalt="領収書:タクシー代 2,400円"
飾りの画像空にする(alt="" と書く。省略はしない)区切りの模様など
横に同じ内容の文字がある画像空にする(同じことを2回読ませない)今回のロゴ。すぐ横に「経費精算システム」と書いてある

width と height も書いておきましょう。画像が読み込まれる前から場所が確保されるので、読み込み途中にページがガタッとずれるのを防げます。

八

申請一覧を組み立てる

ここまでの部品を組み合わせた、今回の完成形です。前回の index.html を、まるごと書き換えます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>申請一覧 | 経費精算システム</title>
</head>
<body>
  <header>
    <p><a href="/"><img src="img/logo.svg" alt="" width="32" height="32"> 経費精算システム</a></p>
    <nav>
      <ul>
        <li><a href="/">申請一覧</a></li>
        <li><a href="approve.html">承認</a></li>
        <li><a href="dashboard.html">ダッシュボード</a></li>
      </ul>
    </nav>
    <p>新井 太郎(申請者)</p>
  </header>

  <main>
    <h1>申請一覧</h1>
    <p><a href="form.html">+ 新規申請</a></p>

    <section>
      <h2>今月のまとめ</h2>
      <ul>
        <li>今月の申請:4 件</li>
        <li>承認待ちの金額:<strong>32,400 円</strong></li>
        <li>差し戻し:1 件</li>
      </ul>
    </section>

    <section>
      <h2>申請の一覧</h2>
      <table>
        <caption>2026年10月の申請(新しい順)</caption>
        <thead>
          <tr>
            <th scope="col">No.</th>
            <th scope="col">申請日</th>
            <th scope="col">件名</th>
            <th scope="col">金額(円)</th>
            <th scope="col">状態</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>0007</td>
            <td><time datetime="2026-10-16">2026-10-16</time></td>
            <td><a href="detail.html">大阪出張(交通費・宿泊費)</a></td>
            <td>32,400</td>
            <td>申請中</td>
          </tr>
          <tr>
            <td>0006</td>
            <td><time datetime="2026-10-15">2026-10-15</time></td>
            <td><a href="detail.html">客先打ち合わせの電車代</a></td>
            <td>3,830</td>
            <td>承認済み</td>
          </tr>
          <tr>
            <td>0005</td>
            <td><time datetime="2026-10-09">2026-10-09</time></td>
            <td><a href="detail.html">技術書の購入</a></td>
            <td>4,180</td>
            <td>差し戻し</td>
          </tr>
          <tr>
            <td>0004</td>
            <td><time datetime="2026-10-02">2026-10-02</time></td>
            <td><a href="detail.html">勉強会の参加費</a></td>
            <td>3,000</td>
            <td>精算済み</td>
          </tr>
        </tbody>
      </table>
    </section>
  </main>

  <footer>
    <p><small>© 2026 KIJI 経費精算システム(練習用)</small></p>
  </footer>
</body>
</html>
申請一覧のページに赤枠と番号を付けたもの。1 header、2 その中の nav、3 h1 の見出し、4 新規申請のリンク、5 今月のまとめの section、6 申請の table、7 footer
完成した骨組みと、使ったタグ。①header(ロゴ・サイト名・利用者)、②nav(メニュー)、③h1、④新規申請のリンク、⑤まとめの section、⑥table、⑦footer(筆者のデモページを、クラウド上の Chromium で撮影。2026年10月17日。ブラウザの枠は説明のために描き足したもの)
  • head の中 ── 前回の charset と title に、meta name="viewport" を足した。スマホで画面の幅に合わせて表示するためのおまじない(詳しくは第5回)。title は「ページ名 | サイト名」の順にすると、タブがたくさん開いていても見分けやすい
  • 同じ「経費精算システム」でも ── header の中ではサイト名なので p、main の中のページ名「申請一覧」が h1。どちらを h1 にするかで迷ったら、「このページの題名はどれか」で決める
  • 件名のリンク先 ── 今は全部 detail.html。どの申請の詳細を出すかは、第10〜12回で URL に番号を付けて渡す

メニューの「承認」「ダッシュボード」や、リンク先の form.html、detail.html は、まだ存在しません。押すと「ファイルが見つからない」と表示されますが、それで正常です。これから順に建てていきます。

九

書いたら検査する

ブラウザは間違いを黙って飲み込んでしまうので、HTML の文法は、専用の検査ツールで確かめます。W3C が公開している Nu Html Checker(validator.w3.org/nu)なら、ファイルを渡すか、コードを貼り付けるだけで使えます。わざと3つ間違えた HTML を検査してみました。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>申請一覧</title>
</head>
<body>
  <header>
    <p><img src="img/logo.svg"> 経費精算システム</p>
  </header>
  <main>
    <h1 id="title">申請一覧</h1>
    <p><strong><a href="form.html">+ 新規申請</strong></a></p>
    <h2 id="title">申請の一覧</h2>
  </main>
</body>
</html>

結果はこうなりました(実際の出力。長い行は折り返し)。

Error: An “img” element must have an “alt” attribute, except under
       certain conditions. For details, consult guidance on
       providing text alternatives for images.  (9行目)
Error: End tag “strong” violates nesting rules.  (13行目)
Error: No “a” element in scope but a “a” end tag seen.  (13行目)
Error: Duplicate ID “title”.  (14行目)
Warning: The first occurrence of ID “title” was here.  (12行目)
Document checking completed.
指摘意味直し方
img に alt がない画像の代わりの文字がないロゴは横にサイト名があるので alt=""
strong の終了タグが入れ子のきまりに違反a より先に strong を閉じている(箱の交差)</a></strong> の順に閉じる
id「title」が重複一つだけのはずの id が2か所にid を変えるか、要らなければ消す

8章の完成形を同じツールにかけると、「No errors found(エラーなし)」と出ます。

NOTE

AI が書いた HTML も、まず検査に通すのがおすすめです。AI は文法の間違いこそ少ないものの、div スープや、見出しの段の飛ばし、alt の書き忘れはよくあります。検査ツールが見つけるのは文法の間違いまでで、「このタグで意味が合っているか」は、人が読んで確かめるしかありません。

結 — 一枚まとめと次回予告

  1. HTML は、タグで中身を挟んで「これは何か」を示す言語。開始タグ・中身・終了タグで一つの要素。属性は 名前="値"。入れ子は、後から開いたものを先に閉じる。
  2. header・nav・main・footer などのランドマークでページを区切る。div は「名無しの箱」で、最後の手段。
  3. 見た目が同じでも、意味のあるタグは、読み上げソフト・テストの道具・AI に、ページの構造をそのまま伝える。
  4. 見出しは段を飛ばさず構造で選ぶ。表は行と列のデータのためだけに使い、caption・thead・th scope を付ける。画像には alt を。
  5. 書いたら Nu Html Checker で検査。AI が書いた HTML も同じ。意味が合っているかは、人が読んで確かめる。

新井骨組みはできました! 次は「新規申請」を押した先ですね。

古井そう、第3回は申請フォームだ。入力欄・選択肢・ファイルの添付。業務システムの本丸だぞ。

参考

ページの写真は、筆者が作ったデモページを2026年10月17日にクラウド上の Chromium(Playwright)で撮影したものです。アクセシビリティの木は Playwright の ariaSnapshot() の実際の出力(長い部分を省略)、検査の結果は Nu Html Checker(vnu-jar 26.10.12)を手元で実行した実際の出力です。target="_blank" の扱いは Can I use の互換表、アウトライン・アルゴリズムの削除は Bruce Lawson 氏の解説(2022年)によります。登場人物は架空の人物です。

いにしえのフロントエンド入門 第2回 — HTML の骨組み:申請一覧を「意味のあるタグ」で組み立てる / 2026.10

← 記事一覧へ