【JavaScript入門】画面レイアウト(index.htmlとstyle.css)

 ここでは、画像付きミニブログ SPA「みんなのつぶやき」の 画面レイアウト を担う index.htmlstyle.css を実装します。
主役となるのは

  • 画像アップロード&プレビュー
  • エフェクト操作ボタン
  • コメント入力・投稿
  • タイムライン(投稿一覧)

 という 4 つの UI ブロックです。フロントエンドは pure HTML/CSS/JavaScript で完結し、バックエンド(index.js)は今回は触れません。まずはフォルダ構成を再確認しておきましょう。

node-js/
 ├─ app/
 │   ├─ index.html        ← ここで作成
 │   ├─ style.css         ← ここで作成
 │   └─ js/               ← JS モジュール群
 │        ├─ common.js
 │        ├─ storage.js
 │        ├─ imageArea.js
 │        ├─ imageEffects.js
 │        ├─ controlArea.js
 │        ├─ commentArea.js
 │        ├─ sendArea.js
 │        ├─ dateFormat.js
 │        └─ postListArea.js
 ├─ index.js               ← API/サーバ側
 ├─ upload_images/         ← 画像保存場所
 └─ post-dat.txt           ← 投稿データ(JSON など)

HTML/CSS の主な要素と役割

種別セレクタ / タグ画面上の部品概要
レイアウトheaderページ最上部のタイトル帯タイトル文字とアイコンを中央配置
main#wrap4 つのカードを縦に並べるラッパflexgap で余白管理
画像エリアsection#imageAreaドラッグ&ドロップ領域 + Canvas画像未選択時は #noView を表示
canvas#viewアップロード画像のプレビューJS 側で display:block に切替
操作ボタンsection#controlAreaセピア/グレースケール等 6 ボタンgrid で自動段組み
button各種アクションtransition で hover 演出
コメントsection#commentAreaテキストエリア入力文字数を #commentStatus に反映
投稿section#sendArea投稿ボタン押下で送信処理
タイムラインsection#postListArea投稿一覧JS が .postListItem を追加
色・装飾:root 変数--primary, --radius など全体の配色/角丸を一元管理

index.html(コメント付き)

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>📸 みんなのつぶやき</title>

  <!-- ▼ ページ全体の見た目を決定するスタイルシート -->
  <link rel="stylesheet" href="style.css" />
</head>

<body>
  <!-- ===== ヘッダー: タイトル帯 ===== -->
  <header>
    <!-- h1 はページ名。絵文字📸はカメラアイコン -->
    <h1>📸 みんなのつぶやき</h1>
  </header>

  <!-- ===== メインコンテンツラッパ ===== -->
  <main id="wrap">
    <!-- === 1. 画像アップロード + プレビュー === -->
    <section id="imageArea" class="card">
      <!-- 画像が未選択のときだけ見せるガイド -->
      <div id="noView">
        <span>ここに画像をドラッグ&ドロップ<br>またはクリックして選択</span>
      </div>

      <!-- 選択後に JS が描画する Canvas -->
      <canvas id="view" width="800" height="420"></canvas>

      <!-- 実際のファイル選択 input。label を使わずエリアクリックを
           トリガにするため JS から `.click()` で呼び出す -->
      <input type="file" id="file" accept=".jpg,.jpeg,.png,.gif" />
    </section>

    <!-- === 2. エフェクト操作ボタン === -->
    <section id="controlArea" class="card btn-grid">
      <!-- 各ボタンは imageEffects.js がクリックイベントを束ねる -->
      <button id="efSepia">セピア</button>        <!-- 色味をセピア化 -->
      <button id="efGray">グレースケール</button><!-- モノクロ化 -->
      <button id="efBlur">ぼかし</button>        <!-- ガウスぼかし -->
      <button id="efBack">元に戻す</button>      <!-- エフェクト解除 -->
      <button id="efSave">保存</button>          <!-- 加工後画像を保存 -->
      <button id="efDel">削除</button>          <!-- アップロード取り消し -->
    </section>

    <!-- === 3. コメント入力 === -->
    <section id="commentArea" class="card">
      <label>コメント <span id="commentStatus"></span></label>
      <!-- data-* 属性は commentArea.js が初期値・最大長を参照 -->
      <textarea id="comment"
        data-default="こんにちは!"
        data-lenmax="100"></textarea>
    </section>

    <!-- === 4. 投稿ボタン === -->
    <section id="sendArea" class="card">
      <button id="send">投稿する</button>
    </section>

    <!-- === 5. タイムライン === -->
    <section id="postListArea" class="card"></section>
  </main>

  <!-- ===== JavaScript モジュール群 =====
       ※依存関係が無いようグローバル関数化済み -->
  <script src="js/common.js"></script>       <!-- 汎用関数 -->
  <script src="js/storage.js"></script>      <!-- LocalStorage ラッパ -->
  <script src="js/imageArea.js"></script>    <!-- アップロード処理 -->
  <script src="js/imageEffects.js"></script> <!-- Canvas エフェクト -->
  <script src="js/controlArea.js"></script>  <!-- ボタン制御 -->
  <script src="js/commentArea.js"></script>  <!-- 文字数カウント等 -->
  <script src="js/sendArea.js"></script>     <!-- 送信処理 -->
  <script src="js/dateFormat.js"></script>   <!-- 日付フォーマッタ -->
  <script src="js/postListArea.js"></script> <!-- タイムライン描画 -->
</body>
</html>

画面部品とタグの対応

  • 画像未選択ガイド (#noView)
    破線枠+中央の <span> が “ドロップしてください” の案内。
  • プレビュー (#view)
    JS 側で display:block に切り替え。アップロード後は #noViewdisplay:none に。
  • 操作ボタン群 (#controlArea<button>)
    6 個のボタンを grid で自動折り返し。スマホでも 2–3 列に配置。
  • コメント入力 (#comment)
    入力長を監視し、残り字数を #commentStatus に反映。
  • 投稿タイムライン (#postListArea)
    JS が .postListItem を append。内部に画像 (.plImage)・本文 (.plBody)・日時 (.plDate)・いいねボタン (.likeBtn) を生成。

style.css(コメント付き)

/* ===== 共通カラーパレット ===== */
:root{
  --bg-main:#f1f5f9;   /* ページ背景 (淡いグレー) */
  --card-bg:#ffffff;   /* カード背景 (白) */
  --primary:#00aaff;   /* アクション用ブルー */
  --danger :#ff4444;   /* 警告用レッド */
  --text  :#333333;    /* 標準文字色 */
  --radius :.65rem;    /* 角丸半径 */
}

/* ===== リセット & ベース ===== */
*{box-sizing:border-box;margin:0;padding:0;}
body{
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
  background:var(--bg-main);
  color:var(--text);
  line-height:1.6;
}
header{
  background:var(--primary);   /* 見出し帯をブランドカラーで塗る */
  color:#fff;
  text-align:center;
  padding:.8rem;
}
h1{font-size:1.4rem;letter-spacing:.05em;}

/* ===== レイアウト ===== */
#wrap{
  max-width:720px;     /* スマホ~タブレット幅で中央寄せ */
  margin:0 auto;
  padding:1rem;
  display:flex;
  flex-direction:column;
  gap:1rem;            /* 各カード間の余白 */
}
.card{
  background:var(--card-bg);
  border-radius:var(--radius);
  padding:1rem;
  box-shadow:0 4px 8px rgb(0 0 0 / .05);
}
.btn-grid{
  display:grid;
  gap:.5rem;
  grid-template-columns:repeat(auto-fill,minmax(6rem,1fr));
}

/* ===== 画像プレビュー領域 ===== */
#noView{
  position:relative;
  display:flex;
  justify-content:center;
  align-items:center;
  text-align:center;
  color:#666;
  height:0;
  padding-top:52.5%;         /* 16:9 比率をパディングで確保 */
  border:2px dashed #ccc;    /* ドラッグ可の示唆に破線 */
  border-radius:var(--radius);
  background:#fafafa;
}
#noView span{
  position:absolute;
  top:50%;left:50%;
  transform:translate(-50%,-50%);
}
#view{
  display:none;              /* 初期は隠す → JS で表示 */
  width:100%;
  border-radius:var(--radius);
}
#file{display:none;}         /* input は画面に出さない */

/* ===== ボタン ===== */
button{
  all:unset;                 /* ブラウザ既定スタイルを除去 */
  background:var(--primary);
  color:#fff;
  text-align:center;
  padding:.75rem .5rem;
  border-radius:var(--radius);
  font-weight:bold;
  cursor:pointer;
  user-select:none;
  transition:opacity .2s;
}
button:hover{opacity:.85;}   /* うっすら暗く */

/* ===== コメント ===== */
textarea{
  width:100%;height:5em;
  padding:.5rem;
  border:1px solid #ccc;
  border-radius:var(--radius);
  resize:vertical;
  font-family:inherit;
}
.inputStatus{font-size:.85rem;color:#666;}
.inputStatusWarn{color:var(--danger);} /* 文字数超過時 */

/* ===== タイムライン ===== */
.postListItem{margin-bottom:1rem;}     /* 投稿カード間隔 */
.plImage img{
  width:100%;
  border-radius:var(--radius);
}
.plBody{padding:.5rem 1rem;}
.plDate{
  font-size:.8rem;
  color:#666;
  text-align:right;
}
.likeBtn{
  all:unset;
  display:inline-flex;
  align-items:center;
  gap:.2rem;
  cursor:pointer;
  font-size:1rem;
  color:#ff6584;            /* 通常のハート色 */
}
.likeBtn.liked{
  color:#ff0033;            /* いいね済みで彩度アップ */
  font-weight:bold;
}

どこを装飾しているか

  • :root の CSS 変数 – 色や角丸を一元管理。将来テーマ変更が楽。
  • #wrap – 最大幅を 720 px に制限し中央寄せ。モバイルでも余白確保。
  • .card – すべての UI ブロックを「カード」化して影と角丸を付与。
  • #noView / #view – 未選択ガイドとプレビュー Canvas の表示切替。padding-top:52.5% で 16:9 アスペクトを保つテクニックはレスポンシブ対応の肝。
  • .btn-grid – grid-template-columns: repeat(auto-fill, minmax(6rem,1fr)) でボタンが画面幅に合わせて自動で折り返す。
  • .likeBtn – inline-flex でハート+数字を水平配置し、状態に応じて色&太字切替。

主要タグ/CSS のポイントまとめ

キーポイント
<canvas id="view">画像プレビューを JavaScript で直接描画。固定サイズではなく幅 100% に縮放。
padding-top:52.5%高さ可変の 16:9 枠を純 CSS で実現(padding trick)。
grid-template-columns:repeat(auto-fill,minmax(6rem,1fr))可変列グリッドでボタンをレスポンシブに並べ替え。
CSS 変数 (--primary, --radius など)色・角丸・影を一箇所で管理し 保守性を高める
.likeBtn.likedclassList.toggle() だけで 状態変化演出(色+太字)が完結。

 次回は ページ全体で共通利用するユーティリティ (common.js) と日付フォーマッタ (dateFormat.js) の内部構造と実装ポイントを解説していきます。