フローティングラベル

入力時にラベルが上へ縮小移動する定番のフォームUI。:placeholder-shown とトランジションだけで実現し、ログインや登録画面に使えます。

#css#form#animation

ライブデモ

使用例(お題: カフェ MOON BREW)

この技法を「カフェ MOON BREW」というテーマのダミーサイトで実際に使った例です。

HTML
<div class="mb-screen">
  <!-- 左:店舗ビジュアル -->
  <aside class="mb-hero">
    <span class="mb-logo">☕ MOON BREW</span>
    <h1 class="mb-hero__title">月夜の<br>ご予約</h1>
    <p class="mb-hero__lead">焙煎したての一杯を、<br>静かな夜の席で。</p>
    <p class="mb-hero__hours">営業 11:00 – 23:00 / 不定休</p>
  </aside>

  <!-- 右:フローティングラベルの予約フォーム -->
  <form class="mb-form" novalidate>
    <h2 class="mb-form__title">来店予約</h2>

    <div class="field">
      <input id="mb-name" class="field__input" type="text" placeholder=" " autocomplete="off">
      <label class="field__label" for="mb-name">お名前</label>
      <span class="field__line"></span>
    </div>

    <div class="field">
      <input id="mb-tel" class="field__input" type="tel" placeholder=" " autocomplete="off">
      <label class="field__label" for="mb-tel">電話番号</label>
      <span class="field__line"></span>
    </div>

    <div class="field field--row">
      <div class="field">
        <input id="mb-date" class="field__input" type="text" placeholder=" " value="6/14(土)">
        <label class="field__label" for="mb-date">来店日</label>
        <span class="field__line"></span>
      </div>
      <div class="field">
        <input id="mb-num" class="field__input" type="text" placeholder=" " value="2名">
        <label class="field__label" for="mb-num">人数</label>
        <span class="field__line"></span>
      </div>
    </div>

    <button class="mb-btn" type="submit">この内容で予約する</button>
  </form>
</div>
CSS
* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
  background: #2b1d12;
  color: #2b1d12;
}

/* 2カラムのカフェ予約画面 */
.mb-screen {
  width: min(680px, 94vw);
  height: 372px;
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  background: #f5ede1;
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 26px 60px -22px rgba(0, 0, 0, 0.6);
}

/* 左ヒーロー:コーヒー写真風グラデ */
.mb-hero {
  position: relative;
  padding: 26px 24px;
  color: #f5ede1;
  background:
    linear-gradient(160deg, rgba(43, 29, 18, 0.86), rgba(43, 29, 18, 0.55)),
    radial-gradient(120% 90% at 80% 10%, #c98a3b 0%, #2b1d12 70%);
}
.mb-logo { font-size: 0.82rem; font-weight: 700; letter-spacing: 0.08em; }
.mb-hero__title { margin: 36px 0 14px; font-size: 1.9rem; line-height: 1.25; font-weight: 800; }
.mb-hero__lead { margin: 0; font-size: 0.86rem; line-height: 1.7; opacity: 0.92; }
.mb-hero__hours {
  position: absolute; left: 24px; bottom: 22px;
  margin: 0; font-size: 0.74rem; letter-spacing: 0.04em;
  color: #e7c79a;
}

/* 右フォーム */
.mb-form { padding: 28px 28px 24px; display: flex; flex-direction: column; }
.mb-form__title {
  margin: 0 0 20px; font-size: 1.05rem; font-weight: 700;
  letter-spacing: 0.04em; color: #2b1d12;
}

.field { position: relative; margin-bottom: 20px; flex: 1; }
.field--row { display: flex; gap: 16px; margin-bottom: 20px; }
.field--row .field { margin-bottom: 0; }

.field__input {
  width: 100%;
  padding: 16px 2px 7px;
  font-size: 0.95rem;
  color: #2b1d12;
  background: transparent;
  border: none;
  border-bottom: 2px solid #d8c4a6;
  outline: none;
}

.field__label {
  position: absolute;
  left: 2px;
  top: 14px;
  font-size: 0.95rem;
  color: #a98c63;
  pointer-events: none;
  transform-origin: left top;
  transition: transform 0.22s ease, color 0.22s ease;
}

/* フォーカス/入力時にラベルを上へ縮小移動(技法の主役) */
.field__input:focus + .field__label,
.field__input:not(:placeholder-shown) + .field__label {
  transform: translateY(-15px) scale(0.78);
  color: #c98a3b;
}

.field__line {
  position: absolute;
  left: 0; bottom: 0;
  height: 2px; width: 100%;
  background: linear-gradient(90deg, #c98a3b, #e7b873);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s ease;
}
.field__input:focus ~ .field__line { transform: scaleX(1); }

.mb-btn {
  margin-top: 6px;
  padding: 13px;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: #f5ede1;
  background: linear-gradient(135deg, #c98a3b, #a96e26);
  border: none;
  border-radius: 11px;
  cursor: pointer;
  box-shadow: 0 12px 22px -10px rgba(169, 110, 38, 0.8);
  transition: transform 0.15s ease;
}
.mb-btn:hover { transform: translateY(-2px); }
.mb-btn:active { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .field__label, .field__line, .mb-btn { transition: none; }
}
JavaScript
// 予約フォームの送信デモ:実送信せずボタンで完了表示
const form = document.querySelector(".mb-form");
if (form) {
  form.addEventListener("submit", (e) => {
    e.preventDefault();
    const btn = form.querySelector(".mb-btn");
    if (!btn) return;
    const label = btn.textContent;
    btn.textContent = "ご予約を承りました ✓";
    btn.style.background = "linear-gradient(135deg,#3f7d4f,#5aa06a)";
    // 1.8秒後に元へ戻す
    setTimeout(() => {
      btn.textContent = label;
      btn.style.background = "";
    }, 1800);
  });
}

コード

HTML
<div class="stage">
  <form class="float-form" novalidate>
    <h2 class="float-title">アカウント作成</h2>

    <!-- 入力欄+ラベルのペア。:placeholder-shown でラベルが浮く -->
    <div class="field">
      <input id="fl-name" class="field__input" type="text" placeholder=" " autocomplete="off">
      <label class="field__label" for="fl-name">お名前</label>
      <span class="field__line"></span>
    </div>

    <div class="field">
      <input id="fl-mail" class="field__input" type="email" placeholder=" " autocomplete="off">
      <label class="field__label" for="fl-mail">メールアドレス</label>
      <span class="field__line"></span>
    </div>

    <div class="field">
      <input id="fl-pass" class="field__input" type="password" placeholder=" " autocomplete="off">
      <label class="field__label" for="fl-pass">パスワード</label>
      <span class="field__line"></span>
    </div>

    <button class="float-btn" type="submit">登録する</button>
  </form>
</div>
CSS
* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
  /* 斜めグラデの落ち着いた背景 */
  background: linear-gradient(135deg, #1e2a44 0%, #25406b 55%, #3a6ea5 100%);
  color: #1f2937;
}

.stage { width: 100%; padding: 24px; display: grid; place-items: center; }

.float-form {
  width: min(340px, 90vw);
  padding: 32px 28px 28px;
  background: rgba(255, 255, 255, 0.96);
  border-radius: 18px;
  box-shadow: 0 24px 60px -20px rgba(8, 18, 40, 0.55);
}

.float-title {
  margin: 0 0 22px;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #1e293b;
}

/* 入力欄まわり */
.field { position: relative; margin-bottom: 22px; }

.field__input {
  width: 100%;
  padding: 18px 2px 8px;
  font-size: 1rem;
  color: #111827;
  background: transparent;
  border: none;
  border-bottom: 2px solid #cbd5e1;
  outline: none;
}

/* ラベルの初期位置=入力欄の中 */
.field__label {
  position: absolute;
  left: 2px;
  top: 16px;
  font-size: 1rem;
  color: #94a3b8;
  pointer-events: none;
  transform-origin: left top;
  transition: transform 0.22s ease, color 0.22s ease;
}

/* フォーカス時/入力済み時にラベルを上へ縮小移動 */
.field__input:focus + .field__label,
.field__input:not(:placeholder-shown) + .field__label {
  transform: translateY(-16px) scale(0.78);
  color: #2563eb;
}

/* 下線をフォーカスで左から伸ばす演出 */
.field__line {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: linear-gradient(90deg, #2563eb, #38bdf8);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s ease;
}
.field__input:focus ~ .field__line { transform: scaleX(1); }

.float-btn {
  width: 100%;
  margin-top: 4px;
  padding: 13px;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: #fff;
  background: linear-gradient(135deg, #2563eb, #4f46e5);
  border: none;
  border-radius: 12px;
  cursor: pointer;
  box-shadow: 0 12px 24px -10px rgba(37, 99, 235, 0.7);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.float-btn:hover { transform: translateY(-2px); box-shadow: 0 18px 30px -10px rgba(37, 99, 235, 0.8); }
.float-btn:active { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .field__label, .field__line, .float-btn { transition: none; }
}
JavaScript
// 送信デモ:ページ遷移を止めて簡単なフィードバックを返す
const form = document.querySelector(".float-form");
if (form) {
  form.addEventListener("submit", (e) => {
    e.preventDefault();
    const btn = form.querySelector(".float-btn");
    if (!btn) return;
    const label = btn.textContent;
    btn.textContent = "登録しました ✓";
    btn.style.background = "linear-gradient(135deg,#16a34a,#22c55e)";
    // 1.6秒後に元へ戻す
    setTimeout(() => {
      btn.textContent = label;
      btn.style.background = "";
    }, 1600);
  });
}

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「フローティングラベル」の効果を追加してください。

# 追加してほしい効果
フローティングラベル(フォーム & 入力)
入力時にラベルが上へ縮小移動する定番のフォームUI。:placeholder-shown とトランジションだけで実現し、ログインや登録画面に使えます。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<div class="stage">
  <form class="float-form" novalidate>
    <h2 class="float-title">アカウント作成</h2>

    <!-- 入力欄+ラベルのペア。:placeholder-shown でラベルが浮く -->
    <div class="field">
      <input id="fl-name" class="field__input" type="text" placeholder=" " autocomplete="off">
      <label class="field__label" for="fl-name">お名前</label>
      <span class="field__line"></span>
    </div>

    <div class="field">
      <input id="fl-mail" class="field__input" type="email" placeholder=" " autocomplete="off">
      <label class="field__label" for="fl-mail">メールアドレス</label>
      <span class="field__line"></span>
    </div>

    <div class="field">
      <input id="fl-pass" class="field__input" type="password" placeholder=" " autocomplete="off">
      <label class="field__label" for="fl-pass">パスワード</label>
      <span class="field__line"></span>
    </div>

    <button class="float-btn" type="submit">登録する</button>
  </form>
</div>

【CSS】
* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
  /* 斜めグラデの落ち着いた背景 */
  background: linear-gradient(135deg, #1e2a44 0%, #25406b 55%, #3a6ea5 100%);
  color: #1f2937;
}

.stage { width: 100%; padding: 24px; display: grid; place-items: center; }

.float-form {
  width: min(340px, 90vw);
  padding: 32px 28px 28px;
  background: rgba(255, 255, 255, 0.96);
  border-radius: 18px;
  box-shadow: 0 24px 60px -20px rgba(8, 18, 40, 0.55);
}

.float-title {
  margin: 0 0 22px;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #1e293b;
}

/* 入力欄まわり */
.field { position: relative; margin-bottom: 22px; }

.field__input {
  width: 100%;
  padding: 18px 2px 8px;
  font-size: 1rem;
  color: #111827;
  background: transparent;
  border: none;
  border-bottom: 2px solid #cbd5e1;
  outline: none;
}

/* ラベルの初期位置=入力欄の中 */
.field__label {
  position: absolute;
  left: 2px;
  top: 16px;
  font-size: 1rem;
  color: #94a3b8;
  pointer-events: none;
  transform-origin: left top;
  transition: transform 0.22s ease, color 0.22s ease;
}

/* フォーカス時/入力済み時にラベルを上へ縮小移動 */
.field__input:focus + .field__label,
.field__input:not(:placeholder-shown) + .field__label {
  transform: translateY(-16px) scale(0.78);
  color: #2563eb;
}

/* 下線をフォーカスで左から伸ばす演出 */
.field__line {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: linear-gradient(90deg, #2563eb, #38bdf8);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s ease;
}
.field__input:focus ~ .field__line { transform: scaleX(1); }

.float-btn {
  width: 100%;
  margin-top: 4px;
  padding: 13px;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: #fff;
  background: linear-gradient(135deg, #2563eb, #4f46e5);
  border: none;
  border-radius: 12px;
  cursor: pointer;
  box-shadow: 0 12px 24px -10px rgba(37, 99, 235, 0.7);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.float-btn:hover { transform: translateY(-2px); box-shadow: 0 18px 30px -10px rgba(37, 99, 235, 0.8); }
.float-btn:active { transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .field__label, .field__line, .float-btn { transition: none; }
}

【JavaScript】
// 送信デモ:ページ遷移を止めて簡単なフィードバックを返す
const form = document.querySelector(".float-form");
if (form) {
  form.addEventListener("submit", (e) => {
    e.preventDefault();
    const btn = form.querySelector(".float-btn");
    if (!btn) return;
    const label = btn.textContent;
    btn.textContent = "登録しました ✓";
    btn.style.background = "linear-gradient(135deg,#16a34a,#22c55e)";
    // 1.6秒後に元へ戻す
    setTimeout(() => {
      btn.textContent = label;
      btn.style.background = "";
    }, 1600);
  });
}

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。