開発情報・ナレッジ

投稿者: 株式会社ゴンドラ 2025年8月14日 (木)

入力体験を劇的に向上。SPIRALフォームにCSSだけで実装する次世代フローティングラベル

フローティングラベルを使用してSPIRALのフォームの入力体験を向上させる方法をご紹介します。
フォームのデザインテンプレートも用意いたしましたので、ダウンロードして試してみてください。

フローティングラベルとは?実装するメリット

まずはデモサイトをご覧ください。
デモサイト
このように入力欄をクリックするとプレースホルダーが入力欄上部に遷移してラベルの役割を果たすアニメーションの手法をフローティングラベルと言います。
伝統的なフォームはラベルと入力欄をテーブル表示したりSPIRALのデフォルトのフォームのように横並びにするデザインが主流です。
しかし、スマートフォンからの入力が一般的になった現代において、これらのデザインは入力項目が多い場合に縦に長くなりすぎてしまいユーザーにスクロールの手間や心理的な圧迫感を与え、ページ離脱の一因になりかねません。
フローティングラベルは、通常時はラベルが表示されないため、フォーム全体をコンパクトに見せることができます。これにより、ユーザースクロール数を削減し、1画面で確認できる項目数を増やすことで、「あとどのくらい入力すれば良いのか」というユーザーの心理的な負担を軽減する大きなメリットがあります。
※今回の実装はHTMLの変更を行わないためCSSで無理やり項目の順序を変更しています。これによりアクセシビリティに課題が生じる場合があります。ご使用用途にご注意ください。

実装に入る前の下準備

先述の通り今回の実装はHTMLの編集をゼロにしているためidやクラスの指定によるスタイル当てをしていません。
その代わりinputタグのname値やSPIRALでデフォルトで当てられているクラスに対してスタイルを適用しています。
以下の通りDBに設定してある差替えキーワードを適用していますが、ご使用のDBに応じてCSSを編集してください。

項目名 使用フィールド 差替キーワード
氏名(姓) テキストフィールド(32 bytes) nameSei
氏名(名) テキストフィールド(32 bytes) nameMei
姓カナ テキストフィールド(32 bytes) kanaSei
メールアドレス メールアドレス(大・小文字無視) mailAddress
パスワード メッセージダイジェスト(SHA256) msg256
電話番号 電話番号 phone
テキストフィールド(32bytes) テキストフィールド(32bytes) text32
テキストフィールド(64bytes) テキストフィールド(64bytes) text64
テキストフィールド(128bytes) テキストフィールド(128bytes) text128
テキストエリア(256bytes) テキストエリア(256bytes) textarea256
セレクト セレクト select
マルチセレクト マルチセレクト multiselect

基本の実装

今回のデザインはフォームに読み込むだけでデザインが完成するCSSを添付しています。
※テキスト系のフィールドやセレクト系、入力項目が分かれる郵便番号や電話番号など網羅しましたが、特殊なフィールドを使用することでデザイン崩れが起きる可能性があります。その際は適宜CSSを調整してください。

手順1:設定デザインの設定
今回のデザインはプレースホルダーが移動してタイトルになるようなアニメーションを実現しています。
そのためタイトルとプレースホルダーを一致させる必要があります。以下画像のように各フィールドに対してフィールド別チェックにて「項目タイトル」と「プレースホルダー」の設定を行なってください。
手順2:設定デザインをDLし既存のCSSを削除
SPIRALで設定が済んだ設定デザインをダウンロードしてstyleタグの中に記述されてある既存のCSSを削除してください。
また、外部のフォントファイルを読み込むため追加の3行を追記してください。
<!DOCTYPE html>
<html id="SMP_STYLE">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title></title>
<link rel="preconnect" href="https://fonts.googleapis.com">  /* 追加 */
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>  /* 追加 */
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap" rel="stylesheet">  /* 追加 */
<style type="text/css">
  /* ここを全て削除 */
</style>
手順3:CSSを適用する(入力、確認、完了共通)
/* --- 基本設定と変数定義 --- */
#SMP_STYLE {
  --primary-color: #007bff;
  --primary-hover-color: #0056b3;
  --border-color: #ced4da;
  --text-color: #495057;
  --label-color: #6c757d;
  --background-color: #f8f9fa;
  --form-background-color: #ffffff;
  --required-color: #dc3545;
  --error-text-color: #721c24;
  --error-background-color: #f8d7da;
  --error-border-color: #f5c6cb;
  --info-background-color: #f1f1f1;
  --font-family: 'Noto Sans JP', sans-serif;
}

/* --- 基本的な要素のスタイル --- */
#SMP_STYLE * {
  box-sizing: border-box;
}

#SMP_STYLE .body {
  margin: 0;
  padding: 2rem;
  font-family: var(--font-family);
  background-color: var(--background-color);
  color: var(--text-color);
}

#SMP_STYLE center {
  text-align: inherit;
}

#SMP_STYLE .body_tbl {
  max-width: 800px;
  margin: 0 auto;
  padding: 0;
  width: 100%;
}

#SMP_STYLE h1 {
  width: 100%;
  padding: 1rem;
  margin: 0;
  background: var(--primary-color);
  color: #ffffff;
  font-size: 1.5rem;
  font-weight: 700;
  text-align: center;
  border-radius: 12px 12px 0 0;
}

#SMP_STYLE .header_rmesg,
#SMP_STYLE .header_emesg {
  width: 100%;
  padding: 1rem;
  text-align: center;
  margin: 0;
  border-bottom: 1px solid #dee2e6;
  border-radius: 0;
}

#SMP_STYLE .header_rmesg {
  background-color: var(--info-background-color);
  color: var(--text-color);
}

#SMP_STYLE .header_emesg {
  background-color: var(--error-background-color);
  border-color: var(--error-border-color);
  color: var(--error-text-color);
  font-weight: 500;
}


/* --- フォームコンテナ --- */
#SMP_STYLE .smp_tmpl {
  width: 100%;
  padding: 2.5rem;
  background-color: var(--form-background-color);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
  border-radius: 0 0 12px 12px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-areas: "A B" "C D" "mail mail" "pass pass" "phone phone" "text1 text1" "text2 text2" "text3 text3" "textarea textarea" "select multiselect";
  gap: 1.75rem 1.5rem;
}

/* --- フォーム要素の共通スタイル --- */
#SMP_STYLE dl.cf {
  position: relative;
  margin: 0;
  padding: 0;
}

#SMP_STYLE dd.data {
  position: static;
  margin: 0;
  font-weight: normal;
}

#SMP_STYLE .input,
#SMP_STYLE textarea,
#SMP_STYLE select {
  width: 100%;
  padding: 1rem 0.8rem;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  font-size: 1rem;
  font-family: var(--font-family);
  color: var(--text-color);
  background-color: var(--form-background-color);
  transition: border-color 0.2s, box-shadow 0.2s;
  resize: vertical;
}

#SMP_STYLE .input:focus,
#SMP_STYLE textarea:focus,
#SMP_STYLE select:focus {
  outline: none;
  border-color: var(--primary-color);
  box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.2);
}

/* --- placeholderの制御 --- */
#SMP_STYLE .input:focus::placeholder,
#SMP_STYLE textarea:focus::placeholder {
  color: transparent;
}

#SMP_STYLE dl.cf:has(dd .input:first-of-type:focus) > dd input[name*=":cf"]::placeholder {
  color: transparent;
}

#SMP_STYLE dl.cf:has(dd input[name*=":cf"]:focus) > dd .input:first-of-type::placeholder {
  color: transparent;
}


/* --- フローティングラベル(dt.title)の実装 --- */
#SMP_STYLE dt.title {
  display: block;
  position: absolute;
  top: 1rem;
  left: 0.8rem;
  font-size: 1rem;
  color: var(--label-color);
  padding: 0 0.25rem;
  margin: 0;
  background-color: var(--form-background-color);
  transition: all 0.2s ease-out;
  pointer-events: none;
  white-space: nowrap;
  border-radius: 4px;
}

/* :has() を使い、入力/フォーカスされたら dt を動かす */
#SMP_STYLE dl.cf:has(dd .input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):focus) > dt.title,
#SMP_STYLE dl.cf:has(dd .input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not(:placeholder-shown)) > dt.title,
#SMP_STYLE dl.cf:has(dd textarea:focus) > dt.title,
#SMP_STYLE dl.cf:has(dd textarea:not(:placeholder-shown)) > dt.title {
  top: -0.7rem;
  left: 0.6rem;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--primary-color);
}

/* 特定フィールドのラベルは最初から上に固定 */
#SMP_STYLE dl.cf:has(dd.phone) > dt.title,
#SMP_STYLE dl.cf:has(dd [name="select"]) > dt.title,
#SMP_STYLE dl.cf:has(dd [name="multiselect"]) > dt.title {
  top: -0.7rem;
  left: 0.6rem;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--label-color);
}

#SMP_STYLE dl.cf:has(dd.phone) dd.data,
#SMP_STYLE dl.cf:has(dd [name="select"]) dd.data,
#SMP_STYLE dl.cf:has(dd [name="multiselect"]) dd.data {
  padding-top: 0.8rem;
}

#SMP_STYLE dl.cf:has(select:not(:has(option:first-child:selected))) > dt.title {
  color: var(--primary-color);
}

#SMP_STYLE dl.cf:has(dd input[name="multiselect"]:checked) > dt.title {
  color: var(--primary-color);
}

#SMP_STYLE dl.cf:has(dd .input.error)>dt.title,
#SMP_STYLE dl.cf:has(dd textarea.error)>dt.title {
  background-color: var(--error-background-color);
  color: var(--error-text-color);
}

/* --- 個別コンポーネントのスタイル --- */
#SMP_STYLE dd.data br {
  display: none;
}

#SMP_STYLE dd.phone ul {
  display: flex;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
  margin: 0;
  align-items: center;
}

#SMP_STYLE dd.phone li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  float: none;
  width: auto;
  padding: 0;
  margin: 0;
}

#SMP_STYLE dd.phone .input {
  text-align: center;
}

#SMP_STYLE .multi2 ul {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  gap: 0.75rem;
  list-style: none;
  margin: 0;
  padding: 1rem;
  border: 1px solid var(--border-color);
  border-radius: 8px;
}

#SMP_STYLE .multi2 li {
  float: none;
  min-width: auto;
  padding: 0;
  margin: 0;
}

#SMP_STYLE .multi2 label {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  cursor: pointer;
  padding: 0;
}

#SMP_STYLE .multi2 label input[type="checkbox"] {
  display: block;
  width: 1.25em;
  height: 1.25em;
  margin: 0;
  accent-color: var(--primary-color);
}

#SMP_STYLE .multi2 label span {
  margin: 0;
  font-weight: normal;
}

/* エラー表示 */
#SMP_STYLE .msg {
  display: block;
  color: var(--error-text-color);
  font-size: 0.9rem;
  font-weight: 500;
  margin-top: 0.5rem;
  padding-left: 0.25rem;
}

#SMP_STYLE .input.error,
#SMP_STYLE textarea.error,
#SMP_STYLE select.error {
  border-color: var(--error-border-color);
  background-color: var(--error-background-color);
}

/* 送信ボタン */
#SMP_STYLE input.submit {
  width: 100%;
  max-width: 320px;
  padding: 0.8rem 1rem;
  font-size: 1.1rem;
  font-weight: 700;
  color: #fff;
  background-color: var(--primary-color);
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: background-color 0.2s, transform 0.1s, box-shadow 0.2s;
  box-shadow: 0 4px 10px rgba(0, 123, 255, 0.2);
  display: block;
  margin: 2rem auto 0;
}

#SMP_STYLE input.submit:hover {
  background-color: var(--primary-hover-color);
  transform: translateY(-2px);
  box-shadow: 0 6px 12px rgba(0, 123, 255, 0.3);
}

/* グリッドエリア定義 */
.smp_tmpl dl.cf:has([name="nameSei"]) {
  grid-area: A;
}

.smp_tmpl dl.cf:has([name="nameMei"]) {
  grid-area: B;
}

.smp_tmpl dl.cf:has([name="kanaSei"]) {
  grid-area: C;
}

.smp_tmpl dl.cf:has([name="kanaMei"]) {
  grid-area: D;
}

.smp_tmpl dl.cf:has([name="mailAddress"]) {
  grid-area: mail;
}

.smp_tmpl dl.cf:has([name="msg256"]) {
  grid-area: pass;
}

.smp_tmpl dl.cf:has([name="phone:a"]) {
  grid-area: phone;
}

.smp_tmpl dl.cf:has([name="text32"]) {
  grid-area: text1;
}

.smp_tmpl dl.cf:has([name="text64"]) {
  grid-area: text2;
}

.smp_tmpl dl.cf:has([name="text128"]) {
  grid-area: text3;
}

.smp_tmpl dl.cf:has([name="textarea256"]) {
  grid-area: textarea;
}

.smp_tmpl dl.cf:has([name="select"]) {
  grid-area: select;
}

.smp_tmpl dl.cf:has([name="multiselect"]) {
  grid-area: multiselect;
}

/* レスポンシブ対応 */
@media screen and (max-width: 767px) {
  #SMP_STYLE .body {
    padding: 1rem 0;
  }

  #SMP_STYLE h1,
  #SMP_STYLE .smp_tmpl {
    width: 100%;
    border-radius: 0;
  }

  #SMP_STYLE .smp_tmpl {
    padding: 1.5rem;
    gap: 1.5rem 0;
    grid-template-columns: 1fr;
    grid-template-areas: "A" "B" "C" "D" "mail" "pass" "phone" "text1" "text2" "text3" "textarea" "select" "multiselect";
  }
}
上記CSSを入力、確認、完了ページのstyleタグの中に挿入してください。
以下のようになります。どのページでも同様のファイルを使用するのでカスタムモジュールに入れて呼び出す形式でも大丈夫です。
<style type="text/css">
/* --- 基本設定と変数定義 --- */
#SMP_STYLE {
  --primary-color: #007bff;
  --primary-hover-color: #0056b3;
  --border-color: #ced4da;
  --text-color: #495057;
  --label-color: #6c757d;
  --background-color: #f8f9fa;
  --form-background-color: #ffffff;
  --required-color: #dc3545;
  --error-text-color: #721c24;
  --error-background-color: #f8d7da;
  --error-border-color: #f5c6cb;
  --info-background-color: #f1f1f1;
  --font-family: 'Noto Sans JP', sans-serif;
}

/* 省略 */

  #SMP_STYLE .smp_tmpl {
    padding: 1.5rem;
    gap: 1.5rem 0;
    grid-template-columns: 1fr;
    grid-template-areas: "A" "B" "C" "D" "mail" "pass" "phone" "text1" "text2" "text3" "textarea" "select" "multiselect";
  }
}
</style>
手順4:CSSを適用する(確認ページ)
/*
 * 確認ページ専用スタイル
 * 共通のCSSを読み込んだ上で、本CSSを読み込んでください。
*/

/* header_textクラスに入力ページと同じスタイルを適用 */
#SMP_STYLE .header_text {
  width: 100%;
  padding: 1rem;
  text-align: center;
  margin: 0;
  border-bottom: 1px solid #dee2e6;
  background-color: var(--info-background-color);
  color: var(--text-color);
}

/* --- フォームコンテナのレイアウトを調整 --- */
#SMP_STYLE .smp_tmpl {
  display: flex;
  flex-direction: column;
  gap: 0;
  grid-template-areas: none;
}

#SMP_STYLE .smp_tmpl dl.cf {
  display: flex;
  align-items: flex-start;
  padding: 1.25rem 0;
  border-bottom: 1px solid #e9ecef;
  margin: 0;
  gap: 1.5rem;
}

#SMP_STYLE .smp_tmpl dl.cf:last-child {
  border-bottom: none;
}

/* ラベル(dt)のスタイル */
#SMP_STYLE .smp_tmpl dt.title {
  display: block;
  flex-shrink: 0;
  width: 300px;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--label-color);
  position: static;
  transform: none;
  background-color: transparent;
  padding: 0 1.5rem 0 0.5rem;
  border-right: 1px solid #e9ecef;
}
/*
  入力ページから継承された、詳細度の高いスタイルを上書きするためのルール
*/
#SMP_STYLE dl.cf:has(dd.phone) > dt.title,
#SMP_STYLE dl.cf:has(dd [name="select"]) > dt.title,
#SMP_STYLE dl.cf:has(dd [name="multiselect"]) > dt.title {
  position: static;
  top: auto;
  left: auto;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--label-color);
  background-color: transparent;
}

/* 入力済みデータ(dd)のスタイル */
#SMP_STYLE .smp_tmpl dd.data {
  font-size: 1rem;
  font-weight: 400;
  color: var(--text-color);
  line-height: 1.6;
  word-break: break-all;
  padding-right: 0.5rem;
}
/* --- 確認ページで、特定のクラスを持つ項目の文字サイズを上書き --- */
#SMP_STYLE .smp_tmpl dd.data.phone,
#SMP_STYLE .smp_tmpl dd.data.multi2 {
  font-size: 1rem;
}

/* --- ボタンのレイアウト調整 --- */
#SMP_STYLE form{
    display: flex;
    justify-content: center;
    gap: 1rem;
    flex-wrap: wrap;
}
#SMP_STYLE input.submit {
    max-width: 320px;
    width: auto;
    flex-grow: 1;
    margin: 0;
}
#SMP_STYLE input.submit[name="SMPFORM_BACK"] {
    background-color: #6c757d;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
    flex-grow: 0;
    width: 100%;
}
#SMP_STYLE input.submit[name="SMPFORM_BACK"]:hover {
    background-color: #5a6268;
}

/* --- レスポンシブ対応 (スマートフォン) --- */
@media screen and (max-width: 767px) {
  #SMP_STYLE .smp_tmpl dl.cf {
    flex-direction: column;
    gap: 0.5rem;
    align-items: flex-start;
    border-right: none;
  }
  #SMP_STYLE .smp_tmpl dt.title {
    font-size: 0.85rem;
    padding-bottom: 0.5rem;
    border-right: none;
    margin-bottom: 0.5rem;
    padding: initial;
  }
  #SMP_STYLE form{
      flex-direction: column;
      align-items: center;
  }
  #SMP_STYLE input.submit,
  #SMP_STYLE input.submit[name="SMPFORM_BACK"] {
      min-width: 280px;
      width: 100%;
      flex-grow: 1;
  }
}
確認ページはstyleタグ内の手順1のCSSの下に上記確認ページ用CSSを適用してください。
手順5:CSSを適用する(完了ページ)
/*
 * 完了ページ専用スタイル
 * 共通CSSを読み込んだ上で、本CSSを読み込んでください。
*/

/* header_textクラスに入力ページと同じスタイルを適用 */
#SMP_STYLE .sub_text {
  padding: 2.5rem;
  text-align: center;
  color: var(--text-color);
}

/* --- フォームコンテナのレイアウトを調整 --- */
#SMP_STYLE .smp_tmpl {
  display: block;
  padding: initial;
  min-height: 100px;
  border: 1px solid var(--border-color);
}
完了ページはstyleタグ内の手順1のCSSの下に上記完了ページ用CSSを適用してください。
※確認ページのCSSの適用は必要ありません。

これにて実装は完了です。ご自身のSPIRAL環境に貼り付けて動作を確認してみてください。

解決しない場合はこちら コンテンツに関しての
要望はこちら